summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-12-01 12:28:43 -0800
committerXavier Ducrohet <xav@android.com>2010-12-02 10:58:19 -0800
commit9eb6d412af6859b6c0bb969c76bbfc48eec8fd4b (patch)
tree99f3c5fc4823142589c4c4d809d55ff84de8d420 /tools/layoutlib
parentc14e7dd8ba15f517a5402ad802377b1d60784416 (diff)
downloadframeworks_base-9eb6d412af6859b6c0bb969c76bbfc48eec8fd4b.zip
frameworks_base-9eb6d412af6859b6c0bb969c76bbfc48eec8fd4b.tar.gz
frameworks_base-9eb6d412af6859b6c0bb969c76bbfc48eec8fd4b.tar.bz2
LayoutLib: Reuse canvas when possible.
Also moved the prepare/cleanupThread methods to Bridge as they are not really specific to the scene anymore. Change-Id: I7b93821913ce4d2fcbe3d8be489be6533ff87e57
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java26
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java30
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java20
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java191
5 files changed, 162 insertions, 110 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index b901e0d..bc6ad64 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -92,13 +92,6 @@ public class Canvas_Delegate {
return mGraphicsStack.peek();
}
- /**
- * Disposes of the {@link Graphics2D} stack.
- */
- public void dispose() {
-
- }
-
// ---- native methods ----
/*package*/ static boolean isOpaque(Canvas thisCanvas) {
@@ -985,6 +978,16 @@ public class Canvas_Delegate {
}
/*package*/ static void finalizer(int nativeCanvas) {
+ // get the delegate from the native int so that it can be disposed.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ assert false;
+ return;
+ }
+
+ canvasDelegate.dispose();
+
+ // remove it from the manager.
sManager.removeDelegate(nativeCanvas);
}
@@ -997,6 +1000,15 @@ public class Canvas_Delegate {
private Canvas_Delegate() {
}
+ /**
+ * Disposes of the {@link Graphics2D} stack.
+ */
+ private void dispose() {
+ while (mGraphicsStack.size() > 0) {
+ mGraphicsStack.pop().dispose();
+ }
+ }
+
private void setBitmap(BufferedImage image) {
mBufferedImage = image;
mGraphicsStack.push(mBufferedImage.createGraphics());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index ca87b4e..759686e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -33,6 +33,7 @@ import com.android.tools.layoutlib.create.OverrideMethod;
import android.graphics.Bitmap;
import android.graphics.Typeface_Delegate;
+import android.os.Looper;
import android.util.Finalizers;
import java.lang.ref.SoftReference;
@@ -294,7 +295,7 @@ public final class Bridge extends LayoutBridge {
SceneResult lastResult = SceneResult.SUCCESS;
LayoutSceneImpl scene = new LayoutSceneImpl(params);
try {
- scene.prepareThread();
+ prepareThread();
lastResult = scene.init(params.getTimeout());
if (lastResult == SceneResult.SUCCESS) {
lastResult = scene.inflate();
@@ -304,7 +305,7 @@ public final class Bridge extends LayoutBridge {
}
} finally {
scene.release();
- scene.cleanupThread();
+ cleanupThread();
}
return new BridgeLayoutScene(scene, lastResult);
@@ -339,6 +340,31 @@ public final class Bridge extends LayoutBridge {
}
/**
+ * Prepares the current thread for rendering.
+ *
+ * Note that while this can be called several time, the first call to {@link #cleanupThread()}
+ * will do the clean-up, and make the thread unable to do further scene actions.
+ */
+ public static void prepareThread() {
+ // we need to make sure the Looper has been initialized for this thread.
+ // this is required for View that creates Handler objects.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ /**
+ * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
+ * <p>
+ * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
+ * call to this will prevent the thread from doing further scene actions
+ */
+ public static void cleanupThread() {
+ // clean up the looper
+ Looper.sThreadLocal.remove();
+ }
+
+ /**
* Returns details of a framework resource from its integer value.
* @param value the integer value
* @return an array of 2 strings containing the resource name and type, or null if the id
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
index f807214..abcbabc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
@@ -64,14 +64,14 @@ public class BridgeLayoutScene extends LayoutScene {
@Override
public SceneResult render(long timeout) {
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(timeout);
if (mLastResult == SceneResult.SUCCESS) {
mLastResult = mScene.render();
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
@@ -81,7 +81,7 @@ public class BridgeLayoutScene extends LayoutScene {
public SceneResult animate(Object targetObject, String animationName,
boolean isFrameworkAnimation, IAnimationListener listener) {
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
if (mLastResult == SceneResult.SUCCESS) {
mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation,
@@ -89,7 +89,7 @@ public class BridgeLayoutScene extends LayoutScene {
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
@@ -106,7 +106,7 @@ public class BridgeLayoutScene extends LayoutScene {
}
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
if (mLastResult == SceneResult.SUCCESS) {
mLastResult = mScene.insertChild((ViewGroup) parentView, childXml,
@@ -114,7 +114,7 @@ public class BridgeLayoutScene extends LayoutScene {
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
@@ -135,7 +135,7 @@ public class BridgeLayoutScene extends LayoutScene {
}
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
if (mLastResult == SceneResult.SUCCESS) {
mLastResult = mScene.moveChild((ViewGroup) parentView, (View) childView,
@@ -143,7 +143,7 @@ public class BridgeLayoutScene extends LayoutScene {
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
@@ -156,14 +156,14 @@ public class BridgeLayoutScene extends LayoutScene {
}
try {
- mScene.prepareThread();
+ Bridge.prepareThread();
mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
if (mLastResult == SceneResult.SUCCESS) {
mLastResult = mScene.removeChild((View) childView, listener);
}
} finally {
mScene.release();
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
return mLastResult;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index d5766ab..80a0b2d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.impl;
import com.android.layoutlib.api.SceneResult;
import com.android.layoutlib.api.LayoutScene.IAnimationListener;
+import com.android.layoutlib.bridge.Bridge;
import android.animation.Animator;
import android.animation.ValueAnimator;
@@ -57,7 +58,7 @@ public class AnimationThread extends Thread {
@Override
public void run() {
- mScene.prepareThread();
+ Bridge.prepareThread();
try {
Handler_Delegate.setCallback(new IHandlerCallback() {
public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
@@ -115,7 +116,7 @@ public class AnimationThread extends Thread {
mListener.done(SceneResult.SUCCESS);
} finally {
Handler_Delegate.setCallback(null);
- mScene.cleanupThread();
+ Bridge.cleanupThread();
}
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 05d207c..bb0a07c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -51,7 +51,6 @@ import android.graphics.Canvas;
import android.graphics.Canvas_Delegate;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
@@ -104,6 +103,9 @@ public class LayoutSceneImpl {
private int mScreenOffset;
private IResourceValue mWindowBackground;
private FrameLayout mViewRoot;
+ private Canvas mCanvas;
+ private int mMeasuredScreenWidth = -1;
+ private int mMeasuredScreenHeight = -1;
// information being returned through the API
private BufferedImage mImage;
@@ -202,31 +204,6 @@ public class LayoutSceneImpl {
}
/**
- * Prepares the current thread for rendering.
- *
- * Note that while this can be called several time, the first call to {@link #cleanupThread()}
- * will do the clean-up, and make the thread unable to do further scene actions.
- */
- public void prepareThread() {
- // we need to make sure the Looper has been initialized for this thread.
- // this is required for View that creates Handler objects.
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
- /**
- * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
- * <p>
- * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
- * call to this will prevent the thread from doing further scene actions
- */
- public void cleanupThread() {
- // clean up the looper
- Looper.sThreadLocal.remove();
- }
-
- /**
* Prepares the scene for action.
* <p>
* This call is blocking if another rendering/inflating is currently happening, and will return
@@ -389,96 +366,107 @@ public class LayoutSceneImpl {
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SceneParams#getRenderingMode()
*/
public SceneResult render() {
checkLock();
try {
- long current = System.currentTimeMillis();
if (mViewRoot == null) {
return new SceneResult(SceneStatus.ERROR_NOT_INFLATED);
}
// measure the views
int w_spec, h_spec;
- int renderScreenWidth = mParams.getScreenWidth();
- int renderScreenHeight = mParams.getScreenHeight();
-
RenderingMode renderingMode = mParams.getRenderingMode();
- if (renderingMode != RenderingMode.NORMAL) {
- // measure the full size needed by the layout.
- w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth,
- renderingMode.isHorizExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY);
- h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
- renderingMode.isVertExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY);
- mViewRoot.measure(w_spec, h_spec);
-
- if (renderingMode.isHorizExpand()) {
- int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
- if (neededWidth > renderScreenWidth) {
- renderScreenWidth = neededWidth;
+ // only do the screen measure when needed.
+ boolean newRenderSize = false;
+ if (mMeasuredScreenWidth == -1) {
+ newRenderSize = true;
+ mMeasuredScreenWidth = mParams.getScreenWidth();
+ mMeasuredScreenHeight = mParams.getScreenHeight();
+
+ if (renderingMode != RenderingMode.NORMAL) {
+ // measure the full size needed by the layout.
+ w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
+ renderingMode.isHorizExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
+ h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
+ renderingMode.isVertExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY);
+ mViewRoot.measure(w_spec, h_spec);
+
+ if (renderingMode.isHorizExpand()) {
+ int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
+ if (neededWidth > mMeasuredScreenWidth) {
+ mMeasuredScreenWidth = neededWidth;
+ }
}
- }
- if (renderingMode.isVertExpand()) {
- int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
- if (neededHeight > renderScreenHeight - mScreenOffset) {
- renderScreenHeight = neededHeight + mScreenOffset;
+ if (renderingMode.isVertExpand()) {
+ int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
+ if (neededHeight > mMeasuredScreenHeight - mScreenOffset) {
+ mMeasuredScreenHeight = neededHeight + mScreenOffset;
+ }
}
}
}
// remeasure with the size we need
// This must always be done before the call to layout
- w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY);
- h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
+ w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY);
+ h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
MeasureSpec.EXACTLY);
mViewRoot.measure(w_spec, h_spec);
// now do the layout.
- mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight);
+ mViewRoot.layout(0, mScreenOffset, mMeasuredScreenWidth, mMeasuredScreenHeight);
// draw the views
// create the BufferedImage into which the layout will be rendered.
- if (mParams.getImageFactory() != null) {
- mImage = mParams.getImageFactory().getImage(renderScreenWidth,
- renderScreenHeight - mScreenOffset);
- } else {
- mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
- BufferedImage.TYPE_INT_ARGB);
- }
+ if (newRenderSize || mCanvas == null) {
+ if (mParams.getImageFactory() != null) {
+ mImage = mParams.getImageFactory().getImage(mMeasuredScreenWidth,
+ mMeasuredScreenHeight - mScreenOffset);
+ } else {
+ mImage = new BufferedImage(mMeasuredScreenWidth,
+ mMeasuredScreenHeight - mScreenOffset, BufferedImage.TYPE_INT_ARGB);
+ }
- if (mParams.isCustomBackgroundEnabled()) {
- Graphics2D gc = mImage.createGraphics();
- gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
- gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset);
- gc.dispose();
- }
+ if (mParams.isCustomBackgroundEnabled()) {
+ Graphics2D gc = mImage.createGraphics();
+ gc.setColor(new Color(mParams.getCustomBackgroundColor(), true));
+ gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight - mScreenOffset);
+ gc.dispose();
+ }
- // create an Android bitmap around the BufferedImage
- Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
- true /*isMutable*/,
- Density.getEnum(mParams.getDensity()));
+ // create an Android bitmap around the BufferedImage
+ Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
+ true /*isMutable*/,
+ Density.getEnum(mParams.getDensity()));
- // create a Canvas around the Android bitmap
- Canvas canvas = new Canvas(bitmap);
- canvas.setDensity(mParams.getDensity());
+ // create a Canvas around the Android bitmap
+ mCanvas = new Canvas(bitmap);
+ mCanvas.setDensity(mParams.getDensity());
+ }
// to set the logger, get the native delegate
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+ Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(mCanvas);
canvasDelegate.setLogger(mParams.getLogger());
- mViewRoot.draw(canvas);
- canvasDelegate.dispose();
+ long preDrawTime = System.currentTimeMillis();
+
+ mViewRoot.draw(mCanvas);
+
+ long drawTime = System.currentTimeMillis();
mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
- System.out.println("rendering (ms): " + (System.currentTimeMillis() - current));
+ System.out.println(String.format("rendering (ms): %03d", drawTime - preDrawTime));
// success!
return SceneResult.SUCCESS;
@@ -567,7 +555,14 @@ public class LayoutSceneImpl {
View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
// add it to the parentView in the correct location
- parentView.addView(child, index);
+ try {
+ parentView.addView(child, index);
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ }
+
+ invalidateRenderingSize();
return render();
}
@@ -581,14 +576,21 @@ public class LayoutSceneImpl {
throw new IllegalArgumentException("beforeSibling not in parentView");
}
- ViewParent parent = childView.getParent();
- if (parent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) parent;
- parentGroup.removeView(childView);
+ try {
+ ViewParent parent = childView.getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup parentGroup = (ViewGroup) parent;
+ parentGroup.removeView(childView);
+ }
+
+ // add it to the parentView in the correct location
+ parentView.addView(childView, index);
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
}
- // add it to the parentView in the correct location
- parentView.addView(childView, index);
+ invalidateRenderingSize();
return render();
}
@@ -596,12 +598,19 @@ public class LayoutSceneImpl {
public SceneResult removeChild(View childView, IAnimationListener listener) {
checkLock();
- ViewParent parent = childView.getParent();
- if (parent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) parent;
- parentGroup.removeView(childView);
+ try {
+ ViewParent parent = childView.getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup parentGroup = (ViewGroup) parent;
+ parentGroup.removeView(childView);
+ }
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
}
+ invalidateRenderingSize();
+
return render();
}
@@ -971,6 +980,10 @@ public class LayoutSceneImpl {
return result;
}
+ private void invalidateRenderingSize() {
+ mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
+ }
+
public BufferedImage getImage() {
return mImage;
}