Merge "Reimplement the native matrix method using the new delegate way."

This commit is contained in:
Xavier Ducrohet
2010-10-27 11:18:02 -07:00
committed by Android (Google) Code Review
8 changed files with 1246 additions and 1070 deletions

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -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 {

View File

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

View File

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

View File

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