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 
17 package android.graphics;
18 
19 import com.android.ide.common.rendering.api.ILayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.layoutlib.bridge.util.CachedPathIteratorFactory;
23 import com.android.layoutlib.bridge.util.CachedPathIteratorFactory.CachedPathIterator;
24 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
25 
26 import java.awt.geom.PathIterator;
27 
28 /**
29  * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
30  * <p/>
31  * Through the layoutlib_create tool, the original native methods of PathMeasure have been
32  * replaced by
33  * calls to methods of the same name in this delegate class.
34  * <p/>
35  * This class behaves like the original native implementation, but in Java, keeping previously
36  * native data into its own objects and mapping them to int that are sent back and forth between it
37  * and the original PathMeasure class.
38  *
39  * @see DelegateManager
40  */
41 public final class PathMeasure_Delegate {
42 
43     // ---- delegate manager ----
44     private static final DelegateManager<PathMeasure_Delegate> sManager =
45             new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
46 
47     // ---- delegate data ----
48     private CachedPathIteratorFactory mOriginalPathIterator;
49 
50     private long mNativePath;
51 
52 
PathMeasure_Delegate(long native_path, boolean forceClosed)53     private PathMeasure_Delegate(long native_path, boolean forceClosed) {
54         mNativePath = native_path;
55         if (native_path != 0) {
56             if (forceClosed) {
57                 // Copy the path and call close
58                 native_path = Path_Delegate.nInit(native_path);
59                 Path_Delegate.nClose(native_path);
60             }
61 
62             Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
63             mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
64                     .getPathIterator(null));
65         }
66     }
67 
68     @LayoutlibDelegate
native_create(long native_path, boolean forceClosed)69     /*package*/ static long native_create(long native_path, boolean forceClosed) {
70         return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
71     }
72 
73     @LayoutlibDelegate
native_destroy(long native_instance)74     /*package*/ static void native_destroy(long native_instance) {
75         sManager.removeJavaReferenceFor(native_instance);
76     }
77 
78     @LayoutlibDelegate
native_getPosTan(long native_instance, float distance, float pos[], float tan[])79     /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
80             float tan[]) {
81         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
82                 "PathMeasure.getPostTan is not supported.", null, null, null);
83         return false;
84     }
85 
86     @LayoutlibDelegate
native_getMatrix(long native_instance, float distance, long native_matrix, int flags)87     /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
88             native_matrix, int flags) {
89         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
90                 "PathMeasure.getMatrix is not supported.", null, null, null);
91         return false;
92     }
93 
94     @LayoutlibDelegate
native_nextContour(long native_instance)95     /*package*/ static boolean native_nextContour(long native_instance) {
96         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
97                 "PathMeasure.nextContour is not supported.", null, null, null);
98         return false;
99     }
100 
101     @LayoutlibDelegate
native_setPath(long native_instance, long native_path, boolean forceClosed)102     /*package*/ static void native_setPath(long native_instance, long native_path, boolean
103             forceClosed) {
104         PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
105         assert pathMeasure != null;
106 
107         if (native_path != 0) {
108             if (forceClosed) {
109                 // Copy the path and call close
110                 native_path = Path_Delegate.nInit(native_path);
111                 Path_Delegate.nClose(native_path);
112             }
113 
114             Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
115             pathMeasure.mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
116                     .getPathIterator(null));
117         }
118 
119         pathMeasure.mNativePath = native_path;
120     }
121 
122     @LayoutlibDelegate
native_getLength(long native_instance)123     /*package*/ static float native_getLength(long native_instance) {
124         PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
125         assert pathMeasure != null;
126 
127         if (pathMeasure.mOriginalPathIterator == null) {
128             return 0;
129         }
130 
131         return pathMeasure.mOriginalPathIterator.iterator().getTotalLength();
132     }
133 
134     @LayoutlibDelegate
native_isClosed(long native_instance)135     /*package*/ static boolean native_isClosed(long native_instance) {
136         PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
137         assert pathMeasure != null;
138 
139         Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
140         if (path == null) {
141             return false;
142         }
143 
144         int type = 0;
145         float segment[] = new float[6];
146         for (PathIterator pi = path.getJavaShape().getPathIterator(null); !pi.isDone(); pi.next()) {
147             type = pi.currentSegment(segment);
148         }
149 
150         // A path is a closed path if the last element is SEG_CLOSE
151         return type == PathIterator.SEG_CLOSE;
152     }
153 
154     @LayoutlibDelegate
native_getSegment(long native_instance, float startD, float stopD, long native_dst_path, boolean startWithMoveTo)155     /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
156             long native_dst_path, boolean startWithMoveTo) {
157         if (startD < 0) {
158             startD = 0;
159         }
160 
161         if (startD >= stopD) {
162             return false;
163         }
164 
165         PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
166         assert pathMeasure != null;
167 
168         CachedPathIterator iterator = pathMeasure.mOriginalPathIterator.iterator();
169         float accLength = startD;
170         boolean isZeroLength = true; // Whether the output has zero length or not
171         float[] points = new float[6];
172 
173         iterator.jumpToSegment(accLength);
174         while (!iterator.isDone() && (stopD - accLength > 0.1f)) {
175             int type = iterator.currentSegment(points, stopD - accLength);
176 
177             if (accLength - iterator.getCurrentSegmentLength() <= stopD) {
178                 if (startWithMoveTo) {
179                     startWithMoveTo = false;
180 
181                     // If this segment is a MOVETO, then we just use that one. If not, then we issue
182                     // a first moveto
183                     if (type != PathIterator.SEG_MOVETO) {
184                         float[] lastPoint = new float[2];
185                         iterator.getCurrentSegmentEnd(lastPoint);
186                         Path_Delegate.nMoveTo(native_dst_path, lastPoint[0], lastPoint[1]);
187                     }
188                 }
189 
190                 isZeroLength = isZeroLength && iterator.getCurrentSegmentLength() > 0;
191                 switch (type) {
192                     case PathIterator.SEG_MOVETO:
193                         Path_Delegate.nMoveTo(native_dst_path, points[0], points[1]);
194                         break;
195                     case PathIterator.SEG_LINETO:
196                         Path_Delegate.nLineTo(native_dst_path, points[0], points[1]);
197                         break;
198                     case PathIterator.SEG_CLOSE:
199                         Path_Delegate.nClose(native_dst_path);
200                         break;
201                     case PathIterator.SEG_CUBICTO:
202                         Path_Delegate.nCubicTo(native_dst_path, points[0], points[1],
203                                 points[2], points[3],
204                                 points[4], points[5]);
205                         break;
206                     case PathIterator.SEG_QUADTO:
207                         Path_Delegate.nQuadTo(native_dst_path, points[0], points[1],
208                                 points[2],
209                                 points[3]);
210                         break;
211                     default:
212                         assert false;
213                 }
214             }
215 
216             accLength += iterator.getCurrentSegmentLength();
217             iterator.next();
218         }
219 
220         return !isZeroLength;
221     }
222 }
223