diff options
author | Xavier Ducrohet <xav@android.com> | 2010-12-01 12:28:43 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2010-12-02 10:58:19 -0800 |
commit | 9eb6d412af6859b6c0bb969c76bbfc48eec8fd4b (patch) | |
tree | 99f3c5fc4823142589c4c4d809d55ff84de8d420 /tools/layoutlib | |
parent | c14e7dd8ba15f517a5402ad802377b1d60784416 (diff) | |
download | frameworks_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')
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; } |