PathParser_Delegate.java revision 491523d52cd8368ef9a92e95fb3e9332bf86a996
1491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta/*
2491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Copyright (C) 2015 The Android Open Source Project
3491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta *
4491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * you may not use this file except in compliance with the License.
6491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * You may obtain a copy of the License at
7491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta *
8491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta *
10491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
11491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * See the License for the specific language governing permissions and
14491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * limitations under the License.
15491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta */
16491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
17491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptapackage android.util;
18491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
19491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog;
20491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge;
21491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport com.android.layoutlib.bridge.impl.DelegateManager;
22491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
24491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport android.annotation.NonNull;
25491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport android.graphics.Path_Delegate;
26491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
27491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport java.awt.geom.Path2D;
28491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport java.util.ArrayList;
29491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport java.util.Arrays;
30491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport java.util.logging.Level;
31491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptaimport java.util.logging.Logger;
32491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
33491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta/**
34491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Delegate that provides implementation for native methods in {@link android.util.PathParser}
35491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * <p/>
36491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Through the layoutlib_create tool, selected methods of PathParser have been replaced by calls to
37491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * methods of the same name in this delegate class.
38491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta *
39491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * Most of the code has been taken from the implementation in
40491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * {@code tools/base/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathParser.java}
41491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta * revision be6fe89a3b686db5a75e7e692a148699973957f3
42491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta */
43491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Guptapublic class PathParser_Delegate {
44491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
45491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static final Logger LOGGER = Logger.getLogger("PathParser");
46491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
47491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    // ---- Builder delegate manager ----
48491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static final DelegateManager<PathParser_Delegate> sManager =
49491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            new DelegateManager<PathParser_Delegate>(PathParser_Delegate.class);
50491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
51491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    // ---- delegate data ----
52491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @NonNull
53491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private PathDataNode[] mPathDataNodes;
54491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
55491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private PathParser_Delegate(@NonNull PathDataNode[] nodes) {
56491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        mPathDataNodes = nodes;
57491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
58491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
59491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
60491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static boolean nParseStringForPath(long pathPtr, @NonNull String pathString, int
61491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            stringLength) {
62491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
63491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (path_delegate == null) {
64491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return false;
65491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
66491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        assert pathString.length() == stringLength;
67491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate.getJavaShape());
68491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return true;
69491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
70491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
71491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
72491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static void nCreatePathFromPathData(long outPathPtr, long pathData) {
73491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        Path_Delegate path_delegate = Path_Delegate.getDelegate(outPathPtr);
74491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate source = sManager.getDelegate(outPathPtr);
75491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (source == null || path_delegate == null) {
76491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return;
77491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
78491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathDataNode.nodesToPath(source.mPathDataNodes, path_delegate.getJavaShape());
79491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
80491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
81491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
82491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static long nCreateEmptyPathData() {
83491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate newDelegate = new PathParser_Delegate(new PathDataNode[0]);
84491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return sManager.addNewDelegate(newDelegate);
85491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
86491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
87491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
88491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static long nCreatePathData(long nativePtr) {
89491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate source = sManager.getDelegate(nativePtr);
90491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (source == null) {
91491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return 0;
92491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
93491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate dest = new PathParser_Delegate(deepCopyNodes(source.mPathDataNodes));
94491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return sManager.addNewDelegate(dest);
95491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
96491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
97491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
98491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static long nCreatePathDataFromString(@NonNull String pathString,
99491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int stringLength) {
100491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        assert pathString.length() == stringLength : "Inconsistent path string length.";
101491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathDataNode[] nodes = createNodesFromPathData(pathString);
102491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate delegate = new PathParser_Delegate(nodes);
103491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return sManager.addNewDelegate(delegate);
104491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
105491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
106491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
107491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
108491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static boolean nInterpolatePathData(long outDataPtr, long fromDataPtr,
109491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            long toDataPtr, float fraction) {
110491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate out = sManager.getDelegate(outDataPtr);
111491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
112491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate to = sManager.getDelegate(toDataPtr);
113491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (out == null || from == null || to == null) {
114491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return false;
115491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
116491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        int length = from.mPathDataNodes.length;
117491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (length != to.mPathDataNodes.length) {
118491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
119491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    "Cannot interpolate path data with different lengths (from " + length + " to " +
120491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            to.mPathDataNodes.length + ").", null);
121491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return false;
122491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
123491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (out.mPathDataNodes.length != length) {
124491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            out.mPathDataNodes = new PathDataNode[length];
125491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
126491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        for (int i = 0; i < length; i++) {
127491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            out.mPathDataNodes[i].interpolatePathDataNode(from.mPathDataNodes[i],
128491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    to.mPathDataNodes[i], fraction);
129491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
130491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return true;
131491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
132491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
133491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
134491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static void nFinalize(long nativePtr) {
135491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        sManager.removeJavaReferenceFor(nativePtr);
136491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
137491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
138491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
139491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static boolean nCanMorph(long fromDataPtr, long toDataPtr) {
140491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "morphing path data isn't " +
141491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                "supported", null, null);
142491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return false;
143491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
144491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
145491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @LayoutlibDelegate
146491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /*package*/ static void nSetPathData(long outDataPtr, long fromDataPtr) {
147491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate out = sManager.getDelegate(outDataPtr);
148491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
149491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (from == null || out == null) {
150491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return;
151491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
152491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        out.mPathDataNodes = deepCopyNodes(from.mPathDataNodes);
153491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
154491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
155491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /**
156491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param pathData The string representing a path, the same as "d" string in svg file.
157491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     *
158491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @return an array of the PathDataNode.
159491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     */
160491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @NonNull
161491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static PathDataNode[] createNodesFromPathData(@NonNull String pathData) {
162491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        int start = 0;
163491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        int end = 1;
164491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
165491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
166491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        while (end < pathData.length()) {
167491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            end = nextStart(pathData, end);
168491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            String s = pathData.substring(start, end).trim();
169491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (s.length() > 0) {
170491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                float[] val = getFloats(s);
171491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                addNode(list, s.charAt(0), val);
172491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
173491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
174491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            start = end;
175491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            end++;
176491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
177491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if ((end - start) == 1 && start < pathData.length()) {
178491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            addNode(list, pathData.charAt(start), new float[0]);
179491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
180491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return list.toArray(new PathDataNode[list.size()]);
181491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
182491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
183491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /**
184491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param source The array of PathDataNode to be duplicated.
185491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     *
186491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @return a deep copy of the <code>source</code>.
187491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     */
188491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @NonNull
189491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static PathDataNode[] deepCopyNodes(@NonNull PathDataNode[] source) {
190491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        PathDataNode[] copy = new PathDataNode[source.length];
191491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        for (int i = 0; i < source.length; i++) {
192491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            copy[i] = new PathDataNode(source[i]);
193491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
194491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return copy;
195491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
196491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
197491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static int nextStart(@NonNull String s, int end) {
198491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        char c;
199491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
200491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        while (end < s.length()) {
201491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            c = s.charAt(end);
202491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // Note that 'e' or 'E' are not valid path commands, but could be
203491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // used for floating point numbers' scientific notation.
204491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // Therefore, when searching for next command, we should ignore 'e'
205491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // and 'E'.
206491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
207491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    && c != 'e' && c != 'E') {
208491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                return end;
209491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
210491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            end++;
211491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
212491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        return end;
213491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
214491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
215491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /**
216491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * Calculate the position of the next comma or space or negative sign
217491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     *
218491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param s the string to search
219491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param start the position to start searching
220491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param result the result of the extraction, including the position of the the starting
221491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * position of next number, whether it is ending with a '-'.
222491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     */
223491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static void extract(@NonNull String s, int start, @NonNull ExtractFloatResult result) {
224491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        // Now looking for ' ', ',', '.' or '-' from the start.
225491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        int currentIndex = start;
226491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        boolean foundSeparator = false;
227491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        result.mEndWithNegOrDot = false;
228491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        boolean secondDot = false;
229491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        boolean isExponential = false;
230491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        for (; currentIndex < s.length(); currentIndex++) {
231491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            boolean isPrevExponential = isExponential;
232491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            isExponential = false;
233491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            char currentChar = s.charAt(currentIndex);
234491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            switch (currentChar) {
235491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case ' ':
236491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case ',':
237491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    foundSeparator = true;
238491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
239491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case '-':
240491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    // The negative sign following a 'e' or 'E' is not a separator.
241491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    if (currentIndex != start && !isPrevExponential) {
242491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        foundSeparator = true;
243491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        result.mEndWithNegOrDot = true;
244491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    }
245491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
246491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case '.':
247491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    if (!secondDot) {
248491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        secondDot = true;
249491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    } else {
250491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        // This is the second dot, and it is considered as a separator.
251491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        foundSeparator = true;
252491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        result.mEndWithNegOrDot = true;
253491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    }
254491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
255491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'e':
256491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'E':
257491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    isExponential = true;
258491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
259491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
260491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (foundSeparator) {
261491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                break;
262491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
263491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
264491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        // When there is nothing found, then we put the end position to the end
265491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        // of the string.
266491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        result.mEndPosition = currentIndex;
267491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
268491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
269491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /**
270491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * Parse the floats in the string. This is an optimized version of
271491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * parseFloat(s.split(",|\\s"));
272491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     *
273491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @param s the string containing a command and list of floats
274491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     *
275491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * @return array of floats
276491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     */
277491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    @NonNull
278491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static float[] getFloats(@NonNull String s) {
279491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
280491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return new float[0];
281491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
282491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        try {
283491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float[] results = new float[s.length()];
284491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int count = 0;
285491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int startPosition = 1;
286491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int endPosition;
287491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
288491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            ExtractFloatResult result = new ExtractFloatResult();
289491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int totalLength = s.length();
290491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
291491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // The startPosition should always be the first character of the
292491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // current number, and endPosition is the character after the current
293491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // number.
294491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            while (startPosition < totalLength) {
295491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                extract(s, startPosition, result);
296491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                endPosition = result.mEndPosition;
297491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
298491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                if (startPosition < endPosition) {
299491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    results[count++] = Float.parseFloat(
300491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            s.substring(startPosition, endPosition));
301491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                }
302491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
303491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                if (result.mEndWithNegOrDot) {
304491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    // Keep the '-' or '.' sign with next number.
305491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    startPosition = endPosition;
306491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                } else {
307491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    startPosition = endPosition + 1;
308491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                }
309491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
310491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return Arrays.copyOf(results, count);
311491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        } catch (NumberFormatException e) {
312491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            throw new RuntimeException("error in parsing \"" + s + "\"", e);
313491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
314491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
315491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
316491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
317491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static void addNode(@NonNull ArrayList<PathDataNode> list, char cmd,
318491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            @NonNull float[] val) {
319491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        list.add(new PathDataNode(cmd, val));
320491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
321491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
322491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static class ExtractFloatResult {
323491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        // We need to return the position of the next separator and whether the
324491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        // next float starts with a '-' or a '.'.
325491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private int mEndPosition;
326491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private boolean mEndWithNegOrDot;
327491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
328491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
329491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    /**
330491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * Each PathDataNode represents one command in the "d" attribute of the svg file. An array of
331491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     * PathDataNode can represent the whole "d" attribute.
332491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta     */
333491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    private static class PathDataNode {
334491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private char mType;
335491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        @NonNull
336491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private float[] mParams;
337491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
338491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private PathDataNode(char type, @NonNull float[] params) {
339491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            mType = type;
340491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            mParams = params;
341491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
342491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
343491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        public char getType() {
344491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return mType;
345491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
346491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
347491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        @NonNull
348491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        public float[] getParams() {
349491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            return mParams;
350491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
351491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
352491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private PathDataNode(@NonNull PathDataNode n) {
353491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            mType = n.mType;
354491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            mParams = Arrays.copyOf(n.mParams, n.mParams.length);
355491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
356491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
357491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /**
358491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * Convert an array of PathDataNode to Path.
359491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         *
360491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param node The source array of PathDataNode.
361491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param path The target Path object.
362491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         */
363491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private static void nodesToPath(@NonNull PathDataNode[] node, @NonNull Path2D path) {
364491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float[] current = new float[6];
365491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            char previousCommand = 'm';
366491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            //noinspection ForLoopReplaceableByForEach
367491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            for (int i = 0; i < node.length; i++) {
368491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
369491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                previousCommand = node[i].mType;
370491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
371491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
372491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
373491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /**
374491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * The current PathDataNode will be interpolated between the <code>nodeFrom</code> and
375491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * <code>nodeTo</code> according to the <code>fraction</code>.
376491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         *
377491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param nodeFrom The start value as a PathDataNode.
378491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param nodeTo The end value as a PathDataNode
379491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param fraction The fraction to interpolate.
380491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         */
381491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private void interpolatePathDataNode(@NonNull PathDataNode nodeFrom,
382491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                @NonNull PathDataNode nodeTo, float fraction) {
383491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            for (int i = 0; i < nodeFrom.mParams.length; i++) {
384491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
385491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        + nodeTo.mParams[i] * fraction;
386491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
387491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
388491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
389491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        @SuppressWarnings("PointlessArithmeticExpression")
390491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private static void addCommand(@NonNull Path2D path, float[] current, char cmd,
391491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                char lastCmd, @NonNull float[] val) {
392491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
393491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int incr = 2;
394491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
395491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float cx = current[0];
396491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float cy = current[1];
397491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float cpx = current[2];
398491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float cpy = current[3];
399491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float loopX = current[4];
400491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            float loopY = current[5];
401491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
402491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            switch (cmd) {
403491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'z':
404491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'Z':
405491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    path.closePath();
406491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    cx = loopX;
407491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    cy = loopY;
408491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'm':
409491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'M':
410491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'l':
411491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'L':
412491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 't':
413491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'T':
414491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    incr = 2;
415491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
416491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'h':
417491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'H':
418491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'v':
419491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'V':
420491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    incr = 1;
421491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
422491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'c':
423491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'C':
424491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    incr = 6;
425491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
426491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 's':
427491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'S':
428491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'q':
429491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'Q':
430491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    incr = 4;
431491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    break;
432491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'a':
433491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                case 'A':
434491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    incr = 7;
435491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
436491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
437491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            for (int k = 0; k < val.length; k += incr) {
438491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                boolean reflectCtrl;
439491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                float tempReflectedX, tempReflectedY;
440491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
441491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                switch (cmd) {
442491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'm':
443491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 0];
444491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 1];
445491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        if (k > 0) {
446491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // According to the spec, if a moveto is followed by multiple
447491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // pairs of coordinates, the subsequent pairs are treated as
448491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // implicit lineto commands.
449491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            path.lineTo(cx, cy);
450491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        } else {
451491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            path.moveTo(cx, cy);
452491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            loopX = cx;
453491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            loopY = cy;
454491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        }
455491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
456491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'M':
457491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 0];
458491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 1];
459491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        if (k > 0) {
460491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // According to the spec, if a moveto is followed by multiple
461491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // pairs of coordinates, the subsequent pairs are treated as
462491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            // implicit lineto commands.
463491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            path.lineTo(cx, cy);
464491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        } else {
465491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            path.moveTo(cx, cy);
466491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            loopX = cx;
467491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            loopY = cy;
468491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        }
469491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
470491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'l':
471491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 0];
472491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 1];
473491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(cx, cy);
474491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
475491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'L':
476491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 0];
477491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 1];
478491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(cx, cy);
479491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
480491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'z':
481491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'Z':
482491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.closePath();
483491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = loopX;
484491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = loopY;
485491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
486491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'h':
487491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 0];
488491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(cx, cy);
489491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
490491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'H':
491491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(val[k + 0], cy);
492491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 0];
493491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
494491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'v':
495491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 0];
496491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(cx, cy);
497491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
498491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'V':
499491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.lineTo(cx, val[k + 0]);
500491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 0];
501491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
502491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'c':
503491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.curveTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
504491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                cy + val[k + 3], cx + val[k + 4], cy + val[k + 5]);
505491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = cx + val[k + 2];
506491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = cy + val[k + 3];
507491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 4];
508491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 5];
509491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
510491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'C':
511491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.curveTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
512491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 4], val[k + 5]);
513491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 4];
514491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 5];
515491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = val[k + 2];
516491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = val[k + 3];
517491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
518491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 's':
519491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' ||
520491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                lastCmd == 'S');
521491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
522491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                * cy - cpy : cy, cx + val[k + 0], cy + val[k + 1], cx
523491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                + val[k + 2], cy + val[k + 3]);
524491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
525491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = cx + val[k + 0];
526491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = cy + val[k + 1];
527491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 2];
528491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 3];
529491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
530491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'S':
531491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' ||
532491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                lastCmd == 'S');
533491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
534491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                        * cy - cpy : cy, val[k + 0], val[k + 1], val[k + 2],
535491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 3]);
536491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = (val[k + 0]);
537491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = (val[k + 1]);
538491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 2];
539491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 3];
540491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
541491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'q':
542491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.quadTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
543491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                cy + val[k + 3]);
544491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = cx + val[k + 0];
545491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = cy + val[k + 1];
546491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        // Note that we have to update cpx first, since cx will be updated here.
547491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 2];
548491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 3];
549491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
550491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'Q':
551491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
552491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 2];
553491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 3];
554491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = val[k + 0];
555491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = val[k + 1];
556491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
557491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 't':
558491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' ||
559491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                lastCmd == 'T');
560491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
561491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
562491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.quadTo(tempReflectedX, tempReflectedY, cx + val[k + 0],
563491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                cy + val[k + 1]);
564491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = tempReflectedX;
565491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = tempReflectedY;
566491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 0];
567491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 1];
568491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
569491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'T':
570491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' ||
571491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                lastCmd == 'T');
572491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
573491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
574491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        path.quadTo(tempReflectedX, tempReflectedY, val[k + 0], val[k + 1]);
575491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 0];
576491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 1];
577491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = tempReflectedX;
578491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = tempReflectedY;
579491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
580491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'a':
581491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
582491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        drawArc(path, cx, cy, val[k + 5] + cx, val[k + 6] + cy,
583491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 0], val[k + 1], val[k + 2], val[k + 3] != 0,
584491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 4] != 0);
585491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx += val[k + 5];
586491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy += val[k + 6];
587491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = cx;
588491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = cy;
589491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
590491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
591491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    case 'A':
592491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        drawArc(path, cx, cy, val[k + 5], val[k + 6], val[k + 0],
593491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 1], val[k + 2], val[k + 3] != 0,
594491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                                val[k + 4] != 0);
595491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cx = val[k + 5];
596491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cy = val[k + 6];
597491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpx = cx;
598491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        cpy = cy;
599491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        break;
600491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
601491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                }
602491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                lastCmd = cmd;
603491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
604491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[0] = cx;
605491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[1] = cy;
606491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[2] = cpx;
607491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[3] = cpy;
608491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[4] = loopX;
609491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            current[5] = loopY;
610491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
611491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
612491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
613491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private static void drawArc(@NonNull Path2D p, float x0, float y0, float x1,
614491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                float y1, float a, float b, float theta, boolean isMoreThanHalf,
615491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                boolean isPositiveArc) {
616491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
617491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            LOGGER.log(Level.FINE, "(" + x0 + "," + y0 + ")-(" + x1 + "," + y1
618491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    + ") {" + a + " " + b + "}");
619491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* Convert rotation angle from degrees to radians */
620491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double thetaD = theta * Math.PI / 180.0f;
621491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* Pre-compute rotation matrix entries */
622491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double cosTheta = Math.cos(thetaD);
623491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sinTheta = Math.sin(thetaD);
624491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* Transform (x0, y0) and (x1, y1) into unit space */
625491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* using (inverse) rotation, followed by (inverse) scale */
626491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
627491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
628491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
629491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
630491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            LOGGER.log(Level.FINE, "unit space (" + x0p + "," + y0p + ")-(" + x1p
631491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    + "," + y1p + ")");
632491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* Compute differences and averages */
633491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double dx = x0p - x1p;
634491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double dy = y0p - y1p;
635491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double xm = (x0p + x1p) / 2;
636491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double ym = (y0p + y1p) / 2;
637491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /* Solve for intersecting unit circles */
638491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double dsq = dx * dx + dy * dy;
639491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (dsq == 0.0) {
640491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                LOGGER.log(Level.FINE, " Points are coincident");
641491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                return; /* Points are coincident */
642491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
643491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double disc = 1.0 / dsq - 1.0 / 4.0;
644491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (disc < 0.0) {
645491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                LOGGER.log(Level.FINE, "Points are too far apart " + dsq);
646491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
647491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta,
648491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        isMoreThanHalf, isPositiveArc);
649491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                return; /* Points are too far apart */
650491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
651491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double s = Math.sqrt(disc);
652491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sdx = s * dx;
653491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sdy = s * dy;
654491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double cx;
655491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double cy;
656491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (isMoreThanHalf == isPositiveArc) {
657491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                cx = xm - sdy;
658491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                cy = ym + sdx;
659491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            } else {
660491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                cx = xm + sdy;
661491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                cy = ym - sdx;
662491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
663491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
664491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
665491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            LOGGER.log(Level.FINE, "eta0 = Math.atan2( " + (y0p - cy) + " , "
666491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    + (x0p - cx) + ") = " + Math.toDegrees(eta0));
667491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
668491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
669491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            LOGGER.log(Level.FINE, "eta1 = Math.atan2( " + (y1p - cy) + " , "
670491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    + (x1p - cx) + ") = " + Math.toDegrees(eta1));
671491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sweep = (eta1 - eta0);
672491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            if (isPositiveArc != (sweep >= 0)) {
673491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                if (sweep > 0) {
674491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    sweep -= 2 * Math.PI;
675491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                } else {
676491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    sweep += 2 * Math.PI;
677491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                }
678491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
679491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
680491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            cx *= a;
681491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            cy *= b;
682491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double tcx = cx;
683491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            cx = cx * cosTheta - cy * sinTheta;
684491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            cy = tcx * sinTheta + cy * cosTheta;
685491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            LOGGER.log(
686491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    Level.FINE,
687491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                    "cx, cy, a, b, x0, y0, thetaD, eta0, sweep = " + cx + " , "
688491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            + cy + " , " + a + " , " + b + " , " + x0 + " , " + y0
689491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            + " , " + Math.toDegrees(thetaD) + " , "
690491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                            + Math.toDegrees(eta0) + " , " + Math.toDegrees(sweep));
691491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
692491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
693491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
694491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
695491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        /**
696491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * Converts an arc to cubic Bezier segments and records them in p.
697491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         *
698491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param p The target for the cubic Bezier segments
699491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param cx The x coordinate center of the ellipse
700491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param cy The y coordinate center of the ellipse
701491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param a The radius of the ellipse in the horizontal direction
702491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param b The radius of the ellipse in the vertical direction
703491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param e1x E(eta1) x coordinate of the starting point of the arc
704491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param e1y E(eta2) y coordinate of the starting point of the arc
705491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param theta The angle that the ellipse bounding rectangle makes with the horizontal
706491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * plane
707491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param start The start angle of the arc on the ellipse
708491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
709491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta         */
710491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        private static void arcToBezier(@NonNull Path2D p, double cx, double cy, double a,
711491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double b, double e1x, double e1y, double theta, double start,
712491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double sweep) {
713491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // Taken from equations at:
714491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // http://spaceroots.org/documents/ellipse/node8.html
715491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // and http://www.spaceroots.org/documents/ellipse/node22.html
716491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            // Maximum of 45 degrees per cubic Bezier segment
717491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
718491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
719491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
720491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double eta1 = start;
721491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double cosTheta = Math.cos(theta);
722491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sinTheta = Math.sin(theta);
723491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double cosEta1 = Math.cos(eta1);
724491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double sinEta1 = Math.sin(eta1);
725491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
726491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
727491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
728491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            double anglePerSegment = sweep / numSegments;
729491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            for (int i = 0; i < numSegments; i++) {
730491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double eta2 = eta1 + anglePerSegment;
731491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double sinEta2 = Math.sin(eta2);
732491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double cosEta2 = Math.cos(eta2);
733491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double e2x = cx + (a * cosTheta * cosEta2)
734491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        - (b * sinTheta * sinEta2);
735491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double e2y = cy + (a * sinTheta * cosEta2)
736491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        + (b * cosTheta * sinEta2);
737491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
738491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
739491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
740491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double alpha = Math.sin(eta2 - eta1)
741491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
742491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double q1x = e1x + alpha * ep1x;
743491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double q1y = e1y + alpha * ep1y;
744491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double q2x = e2x - alpha * ep2x;
745491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                double q2y = e2y - alpha * ep2y;
746491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta
747491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                p.curveTo((float) q1x, (float) q1y, (float) q2x, (float) q2y,
748491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                        (float) e2x, (float) e2y);
749491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                eta1 = eta2;
750491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                e1x = e2x;
751491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                e1y = e2y;
752491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                ep1x = ep2x;
753491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta                ep1y = ep2y;
754491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta            }
755491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta        }
756491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta    }
757491523d52cd8368ef9a92e95fb3e9332bf86a996Deepanshu Gupta}
758