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