Merge "Reimplement the native matrix method using the new delegate way."
This commit is contained in:
committed by
Android (Google) Code Review
commit
0424d69d48
@@ -6,7 +6,7 @@
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
|
||||
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
|
||||
<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
|
||||
<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
|
||||
<classpathentry kind="var" path="ANDROID_PLAT_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
|
||||
<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -18,13 +18,6 @@ package android.graphics;
|
||||
|
||||
import com.android.layoutlib.api.ILayoutLog;
|
||||
|
||||
import android.graphics.DrawFilter;
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.Xfermode;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.FontInfo;
|
||||
import android.graphics.Paint.Style;
|
||||
@@ -42,8 +35,6 @@ import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL;
|
||||
|
||||
/**
|
||||
* Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
|
||||
*/
|
||||
@@ -509,7 +500,7 @@ public class Canvas extends _Original_Canvas {
|
||||
// get the Graphics2D current matrix
|
||||
AffineTransform currentTx = g.getTransform();
|
||||
// get the AffineTransform from the matrix
|
||||
AffineTransform matrixTx = matrix.getTransform();
|
||||
AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
|
||||
|
||||
// combine them so that the matrix is applied after.
|
||||
currentTx.preConcatenate(matrixTx);
|
||||
@@ -969,9 +960,9 @@ public class Canvas extends _Original_Canvas {
|
||||
Graphics2D g = getGraphics2d();
|
||||
|
||||
// and apply the matrix
|
||||
g.setTransform(matrix.getTransform());
|
||||
g.setTransform(Matrix_Delegate.getAffineTransform(matrix));
|
||||
|
||||
if (mLogger != null && matrix.hasPerspective()) {
|
||||
if (mLogger != null && Matrix_Delegate.hasPerspective(matrix)) {
|
||||
mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
|
||||
}
|
||||
}
|
||||
@@ -987,7 +978,7 @@ public class Canvas extends _Original_Canvas {
|
||||
// get its current matrix
|
||||
AffineTransform currentTx = g.getTransform();
|
||||
// get the AffineTransform of the given matrix
|
||||
AffineTransform matrixTx = matrix.getTransform();
|
||||
AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(matrix);
|
||||
|
||||
// combine them so that the given matrix is applied after.
|
||||
currentTx.preConcatenate(matrixTx);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1011
tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
Normal file
1011
tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,13 +31,13 @@ import java.awt.geom.Rectangle2D;
|
||||
* text on a path.
|
||||
*/
|
||||
public class Path {
|
||||
|
||||
|
||||
private FillType mFillType = FillType.WINDING;
|
||||
private GeneralPath mPath = new GeneralPath();
|
||||
|
||||
|
||||
private float mLastX = 0;
|
||||
private float mLastY = 0;
|
||||
|
||||
|
||||
//---------- Custom methods ----------
|
||||
|
||||
public Shape getAwtShape() {
|
||||
@@ -60,7 +60,7 @@ public class Path {
|
||||
public Path(Path src) {
|
||||
mPath.append(src.mPath, false /* connect */);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear any lines and curves from the path, making it empty.
|
||||
* This does NOT change the fill-type setting.
|
||||
@@ -92,7 +92,7 @@ public class Path {
|
||||
EVEN_ODD (GeneralPath.WIND_EVEN_ODD, false),
|
||||
INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
|
||||
INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
|
||||
|
||||
|
||||
FillType(int rule, boolean inverse) {
|
||||
this.rule = rule;
|
||||
this.inverse = inverse;
|
||||
@@ -101,7 +101,7 @@ public class Path {
|
||||
final int rule;
|
||||
final boolean inverse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the path's fill type. This defines how "inside" is
|
||||
* computed. The default value is WINDING.
|
||||
@@ -121,7 +121,7 @@ public class Path {
|
||||
mFillType = ft;
|
||||
mPath.setWindingRule(ft.rule);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the filltype is one of the INVERSE variants
|
||||
*
|
||||
@@ -130,7 +130,7 @@ public class Path {
|
||||
public boolean isInverseFillType() {
|
||||
return mFillType.inverse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggles the INVERSE state of the filltype
|
||||
*/
|
||||
@@ -150,7 +150,7 @@ public class Path {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the path is empty (contains no lines or curves)
|
||||
*
|
||||
@@ -350,7 +350,7 @@ public class Path {
|
||||
boolean forceMoveTo) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append the specified arc to the path as a new contour. If the start of
|
||||
* the path is different from the path's current last point, then an
|
||||
@@ -365,7 +365,7 @@ public class Path {
|
||||
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close the current contour. If the current point is not equal to the
|
||||
* first point of the contour, a line segment is automatically added.
|
||||
@@ -383,13 +383,13 @@ public class Path {
|
||||
CW (0), // must match enum in SkPath.h
|
||||
/** counter-clockwise */
|
||||
CCW (1); // must match enum in SkPath.h
|
||||
|
||||
|
||||
Direction(int ni) {
|
||||
nativeInt = ni;
|
||||
}
|
||||
final int nativeInt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a closed rectangle contour to the path
|
||||
*
|
||||
@@ -400,7 +400,7 @@ public class Path {
|
||||
if (rect == null) {
|
||||
throw new NullPointerException("need rect parameter");
|
||||
}
|
||||
|
||||
|
||||
addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ public class Path {
|
||||
|
||||
// FIXME Need to support direction
|
||||
Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
|
||||
|
||||
|
||||
mPath.append(ovalShape, false /* connect */);
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ public class Path {
|
||||
// FIXME
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a closed round-rectangle contour to the path. Each corner receives
|
||||
* two radius values [X, Y]. The corners are ordered top-left, top-right,
|
||||
@@ -513,7 +513,7 @@ public class Path {
|
||||
// FIXME
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a copy of src to the path, offset by (dx,dy)
|
||||
*
|
||||
@@ -554,11 +554,11 @@ public class Path {
|
||||
*/
|
||||
public void offset(float dx, float dy, Path dst) {
|
||||
GeneralPath newPath = new GeneralPath();
|
||||
|
||||
|
||||
PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
|
||||
|
||||
|
||||
newPath.append(iterator, false /* connect */);
|
||||
|
||||
|
||||
if (dst != null) {
|
||||
dst.mPath = newPath;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.layoutlib.bridge;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
/**
|
||||
* Manages native delegates.
|
||||
*
|
||||
* This is used in conjunction with layoublib_create: certain Android java classes are mere
|
||||
* wrappers around a heavily native based implementation, and we need a way to run these classes
|
||||
* in our Eclipse rendering framework without bringing all the native code from the Android
|
||||
* platform.
|
||||
*
|
||||
* Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
|
||||
* native methods by "delegate calls".
|
||||
*
|
||||
* For example, a native method android.graphics.Matrix.init(...) will actually become
|
||||
* a call to android.graphics.Matrix_Delegate.init(...).
|
||||
*
|
||||
* The Android java classes that use native code uses an int (Java side) to reference native
|
||||
* objects. This int is generally directly the pointer to the C structure counterpart.
|
||||
* Typically a creation method will return such an int, and then this int will be passed later
|
||||
* to a Java method to identify the C object to manipulate.
|
||||
*
|
||||
* Since we cannot use the Java object reference as the int directly, DelegateManager manages the
|
||||
* int -> Delegate class link.
|
||||
*
|
||||
* Native methods usually always have the int as parameters. The first thing the delegate method
|
||||
* will do is call {@link #getDelegate(int)} to get the Java object matching the int.
|
||||
*
|
||||
* Typical native init methods are returning a new int back to the Java class, so
|
||||
* {@link #addDelegate(Object)} does the same.
|
||||
*
|
||||
* @param <T> the delegate class to manage
|
||||
*/
|
||||
public final class DelegateManager<T> {
|
||||
|
||||
private final SparseArray<T> mDelegates = new SparseArray<T>();
|
||||
private int mDelegateCounter = 0;
|
||||
|
||||
/**
|
||||
* Returns the delegate from the given native int.
|
||||
* @param native_object the native int.
|
||||
* @return the delegate or null if not found.
|
||||
*/
|
||||
public T getDelegate(int native_object) {
|
||||
synchronized (mDelegates) {
|
||||
return mDelegates.get(native_object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a delegate to the manager and returns the native int used to identify it.
|
||||
* @param newDelegate the delegate to add
|
||||
* @return a unique native int to identify the delegate
|
||||
*/
|
||||
public int addDelegate(T newDelegate) {
|
||||
synchronized (mDelegates) {
|
||||
int native_object = ++mDelegateCounter;
|
||||
mDelegates.put(native_object, newDelegate);
|
||||
return native_object;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the delegate matching the given native int.
|
||||
* @param native_object the native int.
|
||||
*/
|
||||
public void removeDelegate(int native_object) {
|
||||
synchronized (mDelegates) {
|
||||
mDelegates.remove(native_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.layoutlib.bridge;
|
||||
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
import com.android.tools.layoutlib.create.CreateInfo;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests that native delegate classes implement all the required methods.
|
||||
*
|
||||
* This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
|
||||
* have their native methods reimplemented through a delegate.
|
||||
*
|
||||
* Since the reimplemented methods are not native anymore, we look for the annotation
|
||||
* {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
|
||||
* as the modified class with _Delegate added as a suffix).
|
||||
* If the original native method is not static, then we make sure the delegate method also
|
||||
* include the original class as first parameter (to access "this").
|
||||
*
|
||||
*/
|
||||
public class TestNativeDelegate extends TestCase {
|
||||
|
||||
public void testNativeDelegates() {
|
||||
|
||||
final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
|
||||
final int count = classes.length;
|
||||
for (int i = 0 ; i < count ; i++) {
|
||||
loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
|
||||
// load the classes
|
||||
try {
|
||||
ClassLoader classLoader = TestNativeDelegate.class.getClassLoader();
|
||||
Class<?> originalClass = classLoader.loadClass(originalClassName);
|
||||
Class<?> delegateClass = classLoader.loadClass(delegateClassName);
|
||||
|
||||
compare(originalClass, delegateClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
fail("Failed to load class: " + e.getMessage());
|
||||
} catch (SecurityException e) {
|
||||
fail("Failed to load class: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
|
||||
Method[] originalMethods = originalClass.getDeclaredMethods();
|
||||
|
||||
for (Method originalMethod : originalMethods) {
|
||||
// look for methods that were native: they have the LayoutlibDelegate annotation
|
||||
if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the signature.
|
||||
Class<?>[] parameters = originalMethod.getParameterTypes();
|
||||
|
||||
// if the method is not static, then the class is added as the first parameter
|
||||
// (for "this")
|
||||
if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
|
||||
|
||||
Class<?>[] newParameters = new Class<?>[parameters.length + 1];
|
||||
newParameters[0] = originalClass;
|
||||
System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
|
||||
parameters = newParameters;
|
||||
}
|
||||
|
||||
try {
|
||||
// try to load the method with the given parameter types.
|
||||
delegateClass.getMethod(originalMethod.getName(), parameters);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// compute a full class name that's long but not too long.
|
||||
StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
|
||||
for (int j = 0; j < parameters.length; j++) {
|
||||
Class<?> theClass = parameters[j];
|
||||
sb.append(theClass.getName());
|
||||
int dimensions = 0;
|
||||
while (theClass.isArray()) {
|
||||
dimensions++;
|
||||
theClass = theClass.getComponentType();
|
||||
}
|
||||
for (int i = 0; i < dimensions; i++) {
|
||||
sb.append("[]");
|
||||
}
|
||||
if (j < (parameters.length - 1)) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.tools.layoutlib.create;
|
||||
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
/**
|
||||
* Describes the work to be done by {@link AsmGenerator}.
|
||||
*/
|
||||
@@ -83,7 +85,9 @@ public final class CreateInfo implements ICreateInfo {
|
||||
OverrideMethod.class,
|
||||
MethodListener.class,
|
||||
MethodAdapter.class,
|
||||
CreateInfo.class
|
||||
ICreateInfo.class,
|
||||
CreateInfo.class,
|
||||
LayoutlibDelegate.class
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,8 +103,7 @@ public final class CreateInfo implements ICreateInfo {
|
||||
* The list of classes on which to delegate all native methods.
|
||||
*/
|
||||
private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
|
||||
// TODO: comment out once DelegateClass is working
|
||||
// "android.graphics.Paint"
|
||||
"android.graphics.Matrix",
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -126,7 +129,6 @@ public final class CreateInfo implements ICreateInfo {
|
||||
"android.graphics.ComposeShader", "android.graphics._Original_ComposeShader",
|
||||
"android.graphics.DashPathEffect", "android.graphics._Original_DashPathEffect",
|
||||
"android.graphics.LinearGradient", "android.graphics._Original_LinearGradient",
|
||||
"android.graphics.Matrix", "android.graphics._Original_Matrix",
|
||||
"android.graphics.Paint", "android.graphics._Original_Paint",
|
||||
"android.graphics.Path", "android.graphics._Original_Path",
|
||||
"android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode",
|
||||
|
||||
Reference in New Issue
Block a user