| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.graphics; |
| |
| import com.android.ide.common.rendering.api.LayoutLog; |
| import com.android.layoutlib.bridge.Bridge; |
| import com.android.layoutlib.bridge.impl.DelegateManager; |
| import com.android.layoutlib.bridge.util.CachedPathIteratorFactory; |
| import com.android.tools.layoutlib.annotations.LayoutlibDelegate; |
| |
| import com.android.layoutlib.bridge.util.CachedPathIteratorFactory.CachedPathIterator; |
| |
| import java.awt.geom.PathIterator; |
| |
| /** |
| * Delegate implementing the native methods of {@link android.graphics.PathMeasure} |
| * <p/> |
| * Through the layoutlib_create tool, the original native methods of PathMeasure have been |
| * replaced by |
| * calls to methods of the same name in this delegate class. |
| * <p/> |
| * This class behaves like the original native implementation, but in Java, keeping previously |
| * native data into its own objects and mapping them to int that are sent back and forth between it |
| * and the original PathMeasure class. |
| * |
| * @see DelegateManager |
| */ |
| public final class PathMeasure_Delegate { |
| |
| // ---- delegate manager ---- |
| private static final DelegateManager<PathMeasure_Delegate> sManager = |
| new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class); |
| |
| // ---- delegate data ---- |
| private CachedPathIteratorFactory mOriginalPathIterator; |
| |
| private long mNativePath; |
| |
| |
| private PathMeasure_Delegate(long native_path, boolean forceClosed) { |
| mNativePath = native_path; |
| if (native_path != 0) { |
| if (forceClosed) { |
| // Copy the path and call close |
| native_path = Path_Delegate.init2(native_path); |
| Path_Delegate.native_close(native_path); |
| } |
| |
| Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path); |
| mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape() |
| .getPathIterator(null)); |
| } |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static long native_create(long native_path, boolean forceClosed) { |
| return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed)); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_destroy(long native_instance) { |
| sManager.removeJavaReferenceFor(native_instance); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[], |
| float tan[]) { |
| Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, |
| "PathMeasure.getPostTan is not supported.", null, null); |
| return false; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_getMatrix(long native_instance, float distance, long |
| native_matrix, int flags) { |
| Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, |
| "PathMeasure.getMatrix is not supported.", null, null); |
| return false; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_nextContour(long native_instance) { |
| Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, |
| "PathMeasure.nextContour is not supported.", null, null); |
| return false; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static void native_setPath(long native_instance, long native_path, boolean |
| forceClosed) { |
| PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); |
| assert pathMeasure != null; |
| |
| if (native_path != 0) { |
| if (forceClosed) { |
| // Copy the path and call close |
| native_path = Path_Delegate.init2(native_path); |
| Path_Delegate.native_close(native_path); |
| } |
| |
| Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path); |
| pathMeasure.mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape() |
| .getPathIterator(null)); |
| } |
| |
| pathMeasure.mNativePath = native_path; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static float native_getLength(long native_instance) { |
| PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); |
| assert pathMeasure != null; |
| |
| if (pathMeasure.mOriginalPathIterator == null) { |
| return 0; |
| } |
| |
| return pathMeasure.mOriginalPathIterator.iterator().getTotalLength(); |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_isClosed(long native_instance) { |
| PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); |
| assert pathMeasure != null; |
| |
| Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath); |
| if (path == null) { |
| return false; |
| } |
| |
| int type = 0; |
| float segment[] = new float[6]; |
| for (PathIterator pi = path.getJavaShape().getPathIterator(null); !pi.isDone(); pi.next()) { |
| type = pi.currentSegment(segment); |
| } |
| |
| // A path is a closed path if the last element is SEG_CLOSE |
| return type == PathIterator.SEG_CLOSE; |
| } |
| |
| @LayoutlibDelegate |
| /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD, |
| long native_dst_path, boolean startWithMoveTo) { |
| if (startD < 0) { |
| startD = 0; |
| } |
| |
| if (startD >= stopD) { |
| return false; |
| } |
| |
| PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance); |
| assert pathMeasure != null; |
| |
| CachedPathIterator iterator = pathMeasure.mOriginalPathIterator.iterator(); |
| float accLength = startD; |
| boolean isZeroLength = true; // Whether the output has zero length or not |
| float[] points = new float[6]; |
| |
| iterator.jumpToSegment(accLength); |
| while (!iterator.isDone() && (stopD - accLength > 0.1f)) { |
| int type = iterator.currentSegment(points, stopD - accLength); |
| |
| if (accLength - iterator.getCurrentSegmentLength() <= stopD) { |
| if (startWithMoveTo) { |
| startWithMoveTo = false; |
| |
| // If this segment is a MOVETO, then we just use that one. If not, then we issue |
| // a first moveto |
| if (type != PathIterator.SEG_MOVETO) { |
| float[] lastPoint = new float[2]; |
| iterator.getCurrentSegmentEnd(lastPoint); |
| Path_Delegate.native_moveTo(native_dst_path, lastPoint[0], lastPoint[1]); |
| } |
| } |
| |
| isZeroLength = isZeroLength && iterator.getCurrentSegmentLength() > 0; |
| switch (type) { |
| case PathIterator.SEG_MOVETO: |
| Path_Delegate.native_moveTo(native_dst_path, points[0], points[1]); |
| break; |
| case PathIterator.SEG_LINETO: |
| Path_Delegate.native_lineTo(native_dst_path, points[0], points[1]); |
| break; |
| case PathIterator.SEG_CLOSE: |
| Path_Delegate.native_close(native_dst_path); |
| break; |
| case PathIterator.SEG_CUBICTO: |
| Path_Delegate.native_cubicTo(native_dst_path, points[0], points[1], |
| points[2], points[3], |
| points[4], points[5]); |
| break; |
| case PathIterator.SEG_QUADTO: |
| Path_Delegate.native_quadTo(native_dst_path, points[0], points[1], |
| points[2], |
| points[3]); |
| break; |
| default: |
| assert false; |
| } |
| } |
| |
| accLength += iterator.getCurrentSegmentLength(); |
| iterator.next(); |
| } |
| |
| return !isZeroLength; |
| } |
| } |