PathMeasure_Delegate.java revision 081cebf52b19e848c07fb781b35fa1f96695c311
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
19import com.android.ide.common.rendering.api.LayoutLog;
20import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
22import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23
24import java.awt.geom.PathIterator;
25import java.awt.geom.Point2D;
26
27/**
28 * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
29 * <p/>
30 * Through the layoutlib_create tool, the original native methods of PathMeasure have been
31 * replaced by
32 * calls to methods of the same name in this delegate class.
33 * <p/>
34 * This class behaves like the original native implementation, but in Java, keeping previously
35 * native data into its own objects and mapping them to int that are sent back and forth between it
36 * and the original PathMeasure class.
37 *
38 * @see DelegateManager
39 */
40public final class PathMeasure_Delegate {
41    // ---- delegate manager ----
42    private static final DelegateManager<PathMeasure_Delegate> sManager =
43            new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
44
45    // ---- delegate data ----
46    // This governs how accurate the approximation of the Path is.
47    private static final float PRECISION = 0.002f;
48
49    /**
50     * Array containing the path points components. There are three components for each point:
51     * <ul>
52     *     <li>Fraction along the length of the path that the point resides</li>
53     *     <li>The x coordinate of the point</li>
54     *     <li>The y coordinate of the point</li>
55     * </ul>
56     */
57    private float mPathPoints[];
58    private long mNativePath;
59
60    private PathMeasure_Delegate(long native_path, boolean forceClosed) {
61        mNativePath = native_path;
62        if (forceClosed && mNativePath != 0) {
63            // Copy the path and call close
64            mNativePath = Path_Delegate.init2(native_path);
65            Path_Delegate.native_close(mNativePath);
66        }
67
68        mPathPoints =
69                mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null;
70    }
71
72    @LayoutlibDelegate
73    /*package*/ static long native_create(long native_path, boolean forceClosed) {
74        return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
75    }
76
77    @LayoutlibDelegate
78    /*package*/ static void native_destroy(long native_instance) {
79        sManager.removeJavaReferenceFor(native_instance);
80    }
81
82    @LayoutlibDelegate
83    /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
84            float tan[]) {
85        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
86                "PathMeasure.getPostTan is not supported.", null, null);
87        return false;
88    }
89
90    @LayoutlibDelegate
91    /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
92            native_matrix, int flags) {
93        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
94                "PathMeasure.getMatrix is not supported.", null, null);
95        return false;
96    }
97
98    @LayoutlibDelegate
99    /*package*/ static boolean native_nextContour(long native_instance) {
100        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
101                "PathMeasure.nextContour is not supported.", null, null);
102        return false;
103    }
104
105    @LayoutlibDelegate
106    /*package*/ static void native_setPath(long native_instance, long native_path, boolean
107            forceClosed) {
108        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
109        assert pathMeasure != null;
110
111        if (forceClosed && native_path != 0) {
112            // Copy the path and call close
113            native_path = Path_Delegate.init2(native_path);
114            Path_Delegate.native_close(native_path);
115        }
116        pathMeasure.mNativePath = native_path;
117        pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION);
118    }
119
120    @LayoutlibDelegate
121    /*package*/ static float native_getLength(long native_instance) {
122        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
123        assert pathMeasure != null;
124
125        if (pathMeasure.mPathPoints == null) {
126            return 0;
127        }
128
129        float length = 0;
130        int nPoints = pathMeasure.mPathPoints.length / 3;
131        for (int i = 1; i < nPoints; i++) {
132            length += Point2D.distance(
133                    pathMeasure.mPathPoints[(i - 1) * 3 + 1],
134                    pathMeasure.mPathPoints[(i - 1) * 3 + 2],
135                    pathMeasure.mPathPoints[i*3 + 1],
136                    pathMeasure.mPathPoints[i*3 + 2]);
137        }
138
139        return length;
140    }
141
142    @LayoutlibDelegate
143    /*package*/ static boolean native_isClosed(long native_instance) {
144        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
145        assert pathMeasure != null;
146
147        Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
148        if (path == null) {
149            return false;
150        }
151
152        PathIterator pathIterator = path.getJavaShape().getPathIterator(null);
153
154        int type = 0;
155        float segment[] = new float[6];
156        while (!pathIterator.isDone()) {
157            type = pathIterator.currentSegment(segment);
158            pathIterator.next();
159        }
160
161        // A path is a closed path if the last element is SEG_CLOSE
162        return type == PathIterator.SEG_CLOSE;
163    }
164
165    @LayoutlibDelegate
166    /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
167            long native_dst_path, boolean startWithMoveTo) {
168        if (startD < 0) {
169            startD = 0;
170        }
171
172        if (startD >= stopD) {
173            return false;
174        }
175
176        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
177        assert pathMeasure != null;
178
179        if (pathMeasure.mPathPoints == null) {
180            return false;
181        }
182
183        float accLength = 0;
184        boolean isZeroLength = true; // Whether the output has zero length or not
185        int nPoints = pathMeasure.mPathPoints.length / 3;
186        for (int i = 0; i < nPoints; i++) {
187            float x = pathMeasure.mPathPoints[i * 3 + 1];
188            float y = pathMeasure.mPathPoints[i * 3 + 2];
189            if (accLength >= startD && accLength <= stopD) {
190                if (startWithMoveTo) {
191                    startWithMoveTo = false;
192                    Path_Delegate.native_moveTo(native_dst_path, x, y);
193                } else {
194                    isZeroLength = false;
195                    Path_Delegate.native_lineTo(native_dst_path, x, y);
196                }
197            }
198
199            if (i > 0) {
200                accLength += Point2D.distance(
201                        pathMeasure.mPathPoints[(i - 1) * 3 + 1],
202                        pathMeasure.mPathPoints[(i - 1) * 3 + 2],
203                        pathMeasure.mPathPoints[i * 3 + 1],
204                        pathMeasure.mPathPoints[i * 3 + 2]);
205            }
206        }
207
208        return !isZeroLength;
209    }
210}
211