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.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate implementing the native methods of android.graphics.Path
|
* Delegate implementing the native methods of android.graphics.Path
|
||||||
@@ -173,11 +174,8 @@ public final class Path_Delegate {
|
|||||||
@LayoutlibDelegate
|
@LayoutlibDelegate
|
||||||
/*package*/ static boolean native_isEmpty(long nPath) {
|
/*package*/ static boolean native_isEmpty(long nPath) {
|
||||||
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
|
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
|
||||||
if (pathDelegate == null) {
|
return pathDelegate == null || pathDelegate.isEmpty();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathDelegate.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LayoutlibDelegate
|
@LayoutlibDelegate
|
||||||
@@ -488,54 +486,44 @@ public final class Path_Delegate {
|
|||||||
|
|
||||||
@LayoutlibDelegate
|
@LayoutlibDelegate
|
||||||
/*package*/ static float[] native_approximate(long nPath, float error) {
|
/*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);
|
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
|
||||||
if (pathDelegate == null) {
|
if (pathDelegate == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null);
|
// Get a FlatteningIterator
|
||||||
float[] tmp = new float[6];
|
PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
|
||||||
float[] coords = new float[6];
|
|
||||||
boolean isFirstPoint = true;
|
float segment[] = new float[6];
|
||||||
while (!pathIterator.isDone()) {
|
float totalLength = 0;
|
||||||
int type = pathIterator.currentSegment(tmp);
|
ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
|
||||||
switch (type) {
|
Point2D.Float previousPoint = null;
|
||||||
case PathIterator.SEG_MOVETO:
|
while (!iterator.isDone()) {
|
||||||
case PathIterator.SEG_LINETO:
|
int type = iterator.currentSegment(segment);
|
||||||
store(tmp, coords, 1, isFirstPoint);
|
Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
|
||||||
break;
|
// MoveTo shouldn't affect the length
|
||||||
case PathIterator.SEG_QUADTO:
|
if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
|
||||||
store(tmp, coords, 2, isFirstPoint);
|
totalLength += currentPoint.distance(previousPoint);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
previousPoint = currentPoint;
|
||||||
|
points.add(currentPoint);
|
||||||
|
iterator.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void store(float[] src, float[] dst, int count, boolean isFirst) {
|
int nPoints = points.size();
|
||||||
if (isFirst) {
|
float[] result = new float[nPoints * 3];
|
||||||
dst[0] = 0; // fraction
|
previousPoint = null;
|
||||||
dst[1] = src[0]; // abscissa
|
for (int i = 0; i < nPoints; i++) {
|
||||||
dst[2] = src[1]; // ordinate
|
Point2D.Float point = points.get(i);
|
||||||
}
|
float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
|
||||||
if (count > 1 || !isFirst) {
|
result[i * 3] = distance / totalLength;
|
||||||
dst[3] = 1;
|
result[i * 3 + 1] = point.x;
|
||||||
dst[4] = src[2 * count - 2];
|
result[i * 3 + 2] = point.y;
|
||||||
dst[5] = src[2 * count - 1];
|
|
||||||
|
totalLength += distance;
|
||||||
|
previousPoint = point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Private helper methods ----
|
// ---- Private helper methods ----
|
||||||
@@ -735,6 +723,9 @@ public final class Path_Delegate {
|
|||||||
*/
|
*/
|
||||||
private void cubicTo(float x1, float y1, float x2, float y2,
|
private void cubicTo(float x1, float y1, float x2, float y2,
|
||||||
float x3, float y3) {
|
float x3, float y3) {
|
||||||
|
if (isEmpty()) {
|
||||||
|
mPath.moveTo(0, 0);
|
||||||
|
}
|
||||||
mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
|
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.
|
* The list of methods to rewrite as delegates.
|
||||||
*/
|
*/
|
||||||
public final static String[] DELEGATE_METHODS = new String[] {
|
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.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#obtainStyledAttributes",
|
||||||
"android.content.res.Resources$Theme#resolveAttribute",
|
"android.content.res.Resources$Theme#resolveAttribute",
|
||||||
@@ -235,6 +234,7 @@ public final class CreateInfo implements ICreateInfo {
|
|||||||
"android.graphics.Path",
|
"android.graphics.Path",
|
||||||
"android.graphics.PathDashPathEffect",
|
"android.graphics.PathDashPathEffect",
|
||||||
"android.graphics.PathEffect",
|
"android.graphics.PathEffect",
|
||||||
|
"android.graphics.PathMeasure",
|
||||||
"android.graphics.PixelXorXfermode",
|
"android.graphics.PixelXorXfermode",
|
||||||
"android.graphics.PorterDuffColorFilter",
|
"android.graphics.PorterDuffColorFilter",
|
||||||
"android.graphics.PorterDuffXfermode",
|
"android.graphics.PorterDuffXfermode",
|
||||||
|
|||||||
Reference in New Issue
Block a user