Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a69a3b26 authored by Xavier Ducrohet's avatar Xavier Ducrohet Committed by Android (Google) Code Review
Browse files

Merge "LayoutLib: Reuse canvas when possible."

parents 5e3af5fc 9eb6d412
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -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());
+28 −2
Original line number Diff line number Diff line
@@ -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);
@@ -338,6 +339,31 @@ public final class Bridge extends LayoutBridge {
        return sLock;
    }

    /**
     * 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
+10 −10
Original line number Diff line number Diff line
@@ -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;
+3 −2
Original line number Diff line number Diff line
@@ -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();
        }
    }
}
+102 −89
Original line number Diff line number Diff line
@@ -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;
@@ -201,31 +203,6 @@ public class LayoutSceneImpl {
        return SceneResult.SUCCESS;
    }

    /**
     * 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>
@@ -389,30 +366,35 @@ 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();

            // 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(renderScreenWidth,
                    w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
                            renderingMode.isHorizExpand() ?
                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
                                    : MeasureSpec.EXACTLY);
                h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset,
                    h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight - mScreenOffset,
                            renderingMode.isVertExpand() ?
                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
                                    : MeasureSpec.EXACTLY);
@@ -420,43 +402,45 @@ public class LayoutSceneImpl {

                    if (renderingMode.isHorizExpand()) {
                        int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
                    if (neededWidth > renderScreenWidth) {
                        renderScreenWidth = neededWidth;
                        if (neededWidth > mMeasuredScreenWidth) {
                            mMeasuredScreenWidth = neededWidth;
                        }
                    }

                    if (renderingMode.isVertExpand()) {
                        int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
                    if (neededHeight > renderScreenHeight - mScreenOffset) {
                        renderScreenHeight = neededHeight + mScreenOffset;
                        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 (newRenderSize || mCanvas == null) {
                if (mParams.getImageFactory() != null) {
                mImage = mParams.getImageFactory().getImage(renderScreenWidth,
                        renderScreenHeight - mScreenOffset);
                    mImage = mParams.getImageFactory().getImage(mMeasuredScreenWidth,
                            mMeasuredScreenHeight - mScreenOffset);
                } else {
                mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset,
                        BufferedImage.TYPE_INT_ARGB);
                    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.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight - mScreenOffset);
                    gc.dispose();
                }

@@ -466,19 +450,23 @@ public class LayoutSceneImpl {
                        Density.getEnum(mParams.getDensity()));

                // create a Canvas around the Android bitmap
            Canvas canvas = new Canvas(bitmap);
            canvas.setDensity(mParams.getDensity());
                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
        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,6 +576,7 @@ public class LayoutSceneImpl {
            throw new IllegalArgumentException("beforeSibling not in parentView");
        }

        try {
            ViewParent parent = childView.getParent();
            if (parent instanceof ViewGroup) {
                ViewGroup parentGroup = (ViewGroup) parent;
@@ -589,6 +585,12 @@ public class LayoutSceneImpl {

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

        invalidateRenderingSize();

        return render();
    }
@@ -596,11 +598,18 @@ public class LayoutSceneImpl {
    public SceneResult removeChild(View childView, IAnimationListener listener) {
        checkLock();

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