am 081cebf5: Implement Path.approximate and add PathMeasure support

* commit '081cebf52b19e848c07fb781b35fa1f96695c311':
  Implement Path.approximate and add PathMeasure support
This commit is contained in:
Diego Perez
2015-10-07 16:09:49 +00:00
committed by Android Git Automerger
4 changed files with 247 additions and 105 deletions

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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",