am 081cebf5: Implement Path.approximate and add PathMeasure support
* commit '081cebf52b19e848c07fb781b35fa1f96695c311': Implement Path.approximate and add PathMeasure support
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.animation;
|
||||
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* Delegate providing alternate implementation to static methods in {@link AnimatorInflater}.
|
||||
*/
|
||||
public class AnimatorInflater_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static Animator loadAnimator(Context context, int id)
|
||||
throws NotFoundException {
|
||||
return loadAnimator(context.getResources(), context.getTheme(), id);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id)
|
||||
throws NotFoundException {
|
||||
return loadAnimator(resources, theme, id, 1);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id,
|
||||
float pathErrorScale) throws NotFoundException {
|
||||
// This is a temporary fix to http://b.android.com/77865. This skips loading the
|
||||
// animation altogether.
|
||||
// TODO: Remove this override when Path.approximate() is supported.
|
||||
return new FakeAnimator();
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static ValueAnimator loadAnimator(Resources res, Theme theme,
|
||||
AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
|
||||
throws NotFoundException {
|
||||
return AnimatorInflater.loadAnimator_Original(res, theme, attrs, anim, pathErrorScale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
/**
|
||||
* 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 ----
|
||||
// This governs how accurate the approximation of the Path is.
|
||||
private static final float PRECISION = 0.002f;
|
||||
|
||||
/**
|
||||
* Array containing the path points components. There are three components for each point:
|
||||
* <ul>
|
||||
* <li>Fraction along the length of the path that the point resides</li>
|
||||
* <li>The x coordinate of the point</li>
|
||||
* <li>The y coordinate of the point</li>
|
||||
* </ul>
|
||||
*/
|
||||
private float mPathPoints[];
|
||||
private long mNativePath;
|
||||
|
||||
private PathMeasure_Delegate(long native_path, boolean forceClosed) {
|
||||
mNativePath = native_path;
|
||||
if (forceClosed && mNativePath != 0) {
|
||||
// Copy the path and call close
|
||||
mNativePath = Path_Delegate.init2(native_path);
|
||||
Path_Delegate.native_close(mNativePath);
|
||||
}
|
||||
|
||||
mPathPoints =
|
||||
mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : 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 (forceClosed && native_path != 0) {
|
||||
// Copy the path and call close
|
||||
native_path = Path_Delegate.init2(native_path);
|
||||
Path_Delegate.native_close(native_path);
|
||||
}
|
||||
pathMeasure.mNativePath = native_path;
|
||||
pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static float native_getLength(long native_instance) {
|
||||
PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
|
||||
assert pathMeasure != null;
|
||||
|
||||
if (pathMeasure.mPathPoints == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float length = 0;
|
||||
int nPoints = pathMeasure.mPathPoints.length / 3;
|
||||
for (int i = 1; i < nPoints; i++) {
|
||||
length += Point2D.distance(
|
||||
pathMeasure.mPathPoints[(i - 1) * 3 + 1],
|
||||
pathMeasure.mPathPoints[(i - 1) * 3 + 2],
|
||||
pathMeasure.mPathPoints[i*3 + 1],
|
||||
pathMeasure.mPathPoints[i*3 + 2]);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
PathIterator pathIterator = path.getJavaShape().getPathIterator(null);
|
||||
|
||||
int type = 0;
|
||||
float segment[] = new float[6];
|
||||
while (!pathIterator.isDone()) {
|
||||
type = pathIterator.currentSegment(segment);
|
||||
pathIterator.next();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
if (pathMeasure.mPathPoints == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float accLength = 0;
|
||||
boolean isZeroLength = true; // Whether the output has zero length or not
|
||||
int nPoints = pathMeasure.mPathPoints.length / 3;
|
||||
for (int i = 0; i < nPoints; i++) {
|
||||
float x = pathMeasure.mPathPoints[i * 3 + 1];
|
||||
float y = pathMeasure.mPathPoints[i * 3 + 2];
|
||||
if (accLength >= startD && accLength <= stopD) {
|
||||
if (startWithMoveTo) {
|
||||
startWithMoveTo = false;
|
||||
Path_Delegate.native_moveTo(native_dst_path, x, y);
|
||||
} else {
|
||||
isZeroLength = false;
|
||||
Path_Delegate.native_lineTo(native_dst_path, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
accLength += Point2D.distance(
|
||||
pathMeasure.mPathPoints[(i - 1) * 3 + 1],
|
||||
pathMeasure.mPathPoints[(i - 1) * 3 + 2],
|
||||
pathMeasure.mPathPoints[i * 3 + 1],
|
||||
pathMeasure.mPathPoints[i * 3 + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
return !isZeroLength;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Delegate implementing the native methods of android.graphics.Path
|
||||
@@ -173,11 +174,8 @@ public final class Path_Delegate {
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static boolean native_isEmpty(long nPath) {
|
||||
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
|
||||
if (pathDelegate == null) {
|
||||
return true;
|
||||
}
|
||||
return pathDelegate == null || pathDelegate.isEmpty();
|
||||
|
||||
return pathDelegate.isEmpty();
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -488,54 +486,44 @@ public final class Path_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static float[] native_approximate(long nPath, float error) {
|
||||
Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported",
|
||||
null);
|
||||
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
|
||||
if (pathDelegate == null) {
|
||||
return null;
|
||||
}
|
||||
PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null);
|
||||
float[] tmp = new float[6];
|
||||
float[] coords = new float[6];
|
||||
boolean isFirstPoint = true;
|
||||
while (!pathIterator.isDone()) {
|
||||
int type = pathIterator.currentSegment(tmp);
|
||||
switch (type) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
case PathIterator.SEG_LINETO:
|
||||
store(tmp, coords, 1, isFirstPoint);
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
store(tmp, coords, 2, isFirstPoint);
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
store(tmp, coords, 3, isFirstPoint);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
// No points returned.
|
||||
}
|
||||
isFirstPoint = false;
|
||||
pathIterator.next();
|
||||
}
|
||||
if (isFirstPoint) {
|
||||
// No points found
|
||||
return new float[0];
|
||||
} else {
|
||||
return coords;
|
||||
}
|
||||
}
|
||||
// Get a FlatteningIterator
|
||||
PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
|
||||
|
||||
private static void store(float[] src, float[] dst, int count, boolean isFirst) {
|
||||
if (isFirst) {
|
||||
dst[0] = 0; // fraction
|
||||
dst[1] = src[0]; // abscissa
|
||||
dst[2] = src[1]; // ordinate
|
||||
float segment[] = new float[6];
|
||||
float totalLength = 0;
|
||||
ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
|
||||
Point2D.Float previousPoint = null;
|
||||
while (!iterator.isDone()) {
|
||||
int type = iterator.currentSegment(segment);
|
||||
Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
|
||||
// MoveTo shouldn't affect the length
|
||||
if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
|
||||
totalLength += currentPoint.distance(previousPoint);
|
||||
}
|
||||
previousPoint = currentPoint;
|
||||
points.add(currentPoint);
|
||||
iterator.next();
|
||||
}
|
||||
if (count > 1 || !isFirst) {
|
||||
dst[3] = 1;
|
||||
dst[4] = src[2 * count - 2];
|
||||
dst[5] = src[2 * count - 1];
|
||||
|
||||
int nPoints = points.size();
|
||||
float[] result = new float[nPoints * 3];
|
||||
previousPoint = null;
|
||||
for (int i = 0; i < nPoints; i++) {
|
||||
Point2D.Float point = points.get(i);
|
||||
float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
|
||||
result[i * 3] = distance / totalLength;
|
||||
result[i * 3 + 1] = point.x;
|
||||
result[i * 3 + 2] = point.y;
|
||||
|
||||
totalLength += distance;
|
||||
previousPoint = point;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---- Private helper methods ----
|
||||
@@ -735,6 +723,9 @@ public final class Path_Delegate {
|
||||
*/
|
||||
private void cubicTo(float x1, float y1, float x2, float y2,
|
||||
float x3, float y3) {
|
||||
if (isEmpty()) {
|
||||
mPath.moveTo(0, 0);
|
||||
}
|
||||
mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,6 @@ public final class CreateInfo implements ICreateInfo {
|
||||
* The list of methods to rewrite as delegates.
|
||||
*/
|
||||
public final static String[] DELEGATE_METHODS = new String[] {
|
||||
"android.animation.AnimatorInflater#loadAnimator", // TODO: remove when Path.approximate() is supported.
|
||||
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
|
||||
"android.content.res.Resources$Theme#obtainStyledAttributes",
|
||||
"android.content.res.Resources$Theme#resolveAttribute",
|
||||
@@ -235,6 +234,7 @@ public final class CreateInfo implements ICreateInfo {
|
||||
"android.graphics.Path",
|
||||
"android.graphics.PathDashPathEffect",
|
||||
"android.graphics.PathEffect",
|
||||
"android.graphics.PathMeasure",
|
||||
"android.graphics.PixelXorXfermode",
|
||||
"android.graphics.PorterDuffColorFilter",
|
||||
"android.graphics.PorterDuffXfermode",
|
||||
|
||||
Reference in New Issue
Block a user