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