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

Commit e0e69f87 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Using RenderNode for creating snapshot of a View, so that hardware...

Merge "Using RenderNode for creating snapshot of a View, so that hardware bitmaps are drawn properly"
parents e4a03006 d1b287e5
Loading
Loading
Loading
Loading
+35 −58
Original line number Diff line number Diff line
@@ -18967,7 +18967,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *
     * @hide
     */
    public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
    public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) {
        int width = mRight - mLeft;
        int height = mBottom - mTop;
@@ -18976,37 +18976,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        width = (int) ((width * scale) + 0.5f);
        height = (int) ((height * scale) + 0.5f);
        Bitmap bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                width > 0 ? width : 1, height > 0 ? height : 1, quality);
        if (bitmap == null) {
            throw new OutOfMemoryError();
        }
        Resources resources = getResources();
        if (resources != null) {
            bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
        }
        Canvas oldCanvas = null;
        try {
            Canvas canvas = canvasProvider.getCanvas(this,
                    width > 0 ? width : 1, height > 0 ? height : 1);
        Canvas canvas;
            if (attachInfo != null) {
            canvas = attachInfo.mCanvas;
            if (canvas == null) {
                canvas = new Canvas();
            }
            canvas.setBitmap(bitmap);
                oldCanvas = attachInfo.mCanvas;
                // Temporarily clobber the cached Canvas in case one of our children
                // is also using a drawing cache. Without this, the children would
                // steal the canvas by attaching their own bitmap to it and bad, bad
                // things would happen (invisible views, corrupted drawings, etc.)
                attachInfo.mCanvas = null;
        } else {
            // This case should hopefully never or seldom happen
            canvas = new Canvas(bitmap);
        }
        boolean enabledHwBitmapsInSwMode = canvas.isHwBitmapsInSwModeEnabled();
        canvas.setHwBitmapsInSwModeEnabled(true);
        if ((backgroundColor & 0xff000000) != 0) {
            bitmap.eraseColor(backgroundColor);
            }
            computeScroll();
@@ -19030,17 +19011,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
            mPrivateFlags = flags;
            canvas.restoreToCount(restoreCount);
        canvas.setBitmap(null);
        canvas.setHwBitmapsInSwModeEnabled(enabledHwBitmapsInSwMode);
        if (attachInfo != null) {
            // Restore the cached Canvas for our siblings
            attachInfo.mCanvas = canvas;
            return canvasProvider.createBitmap();
        } finally {
            if (oldCanvas != null) {
                attachInfo.mCanvas = oldCanvas;
            }
        }
        return bitmap;
    }
    /**
+92 −10
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
@@ -773,17 +774,16 @@ public class ViewDebug {
            final CountDownLatch latch = new CountDownLatch(1);
            final Bitmap[] cache = new Bitmap[1];

            captureView.post(new Runnable() {
                public void run() {
            captureView.post(() -> {
                try {
                        cache[0] = captureView.createSnapshot(
                                Bitmap.Config.ARGB_8888, 0, skipChildren);
                    CanvasProvider provider = captureView.isHardwareAccelerated()
                            ? new HardwareCanvasProvider() : new SoftwareCanvasProvider();
                    cache[0] = captureView.createSnapshot(provider, skipChildren);
                } catch (OutOfMemoryError e) {
                    Log.w("View", "Out of memory for bitmap");
                } finally {
                    latch.countDown();
                }
                }
            });

            try {
@@ -1740,4 +1740,86 @@ public class ViewDebug {
            }
        });
    }

    /**
     * @hide
     */
    public static class SoftwareCanvasProvider implements CanvasProvider {

        private Canvas mCanvas;
        private Bitmap mBitmap;
        private boolean mEnabledHwBitmapsInSwMode;

        @Override
        public Canvas getCanvas(View view, int width, int height) {
            mBitmap = Bitmap.createBitmap(view.getResources().getDisplayMetrics(),
                    width, height, Bitmap.Config.ARGB_8888);
            if (mBitmap == null) {
                throw new OutOfMemoryError();
            }
            mBitmap.setDensity(view.getResources().getDisplayMetrics().densityDpi);

            if (view.mAttachInfo != null) {
                mCanvas = view.mAttachInfo.mCanvas;
            }
            if (mCanvas == null) {
                mCanvas = new Canvas();
            }
            mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
            mCanvas.setBitmap(mBitmap);
            return mCanvas;
        }

        @Override
        public Bitmap createBitmap() {
            mCanvas.setBitmap(null);
            mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
            return mBitmap;
        }
    }

    /**
     * @hide
     */
    public static class HardwareCanvasProvider implements CanvasProvider {

        private View mView;
        private Point mSize;
        private RenderNode mNode;
        private DisplayListCanvas mCanvas;

        @Override
        public Canvas getCanvas(View view, int width, int height) {
            mView = view;
            mSize = new Point(width, height);
            mNode = RenderNode.create("ViewDebug", mView);
            mNode.setLeftTopRightBottom(0, 0, width, height);
            mNode.setClipToBounds(false);
            mCanvas = mNode.start(width, height);
            return mCanvas;
        }

        @Override
        public Bitmap createBitmap() {
            mNode.end(mCanvas);
            return ThreadedRenderer.createHardwareBitmap(mNode, mSize.x, mSize.y);
        }
    }

    /**
     * @hide
     */
    public interface CanvasProvider {

        /**
         * Returns a canvas which can be used to draw {@param view}
         */
        Canvas getCanvas(View view, int width, int height);

        /**
         * Creates a bitmap from previously returned canvas
         * @return
         */
        Bitmap createBitmap();
    }
}
+10 −10
Original line number Diff line number Diff line
@@ -3863,7 +3863,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * @hide
     */
    @Override
    public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
    public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) {
        int count = mChildrenCount;
        int[] visibilities = null;

@@ -3879,8 +3879,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            }
        }

        Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);

        try {
            return super.createSnapshot(canvasProvider, skipChildren);
        } finally {
            if (skipChildren) {
                for (int i = 0; i < count; i++) {
                    View child = getChildAt(i);
@@ -3888,8 +3889,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                            | (visibilities[i] & View.VISIBILITY_MASK);
                }
            }

        return b;
        }
    }

    /** Return true if this ViewGroup is laying out using optical bounds. */
+29 −6
Original line number Diff line number Diff line
@@ -25,10 +25,14 @@ import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseIntArray;
import android.view.ViewDebug.CanvasProvider;
import android.view.ViewDebug.HardwareCanvasProvider;
import android.view.ViewDebug.SoftwareCanvasProvider;

import com.android.frameworks.coretests.R;

import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -55,23 +59,42 @@ public class ViewCaptureTest {
    @Before
    public void setUp() throws Exception {
        mActivity = mActivityRule.getActivity();
        mViewToCapture = (ViewGroup) mActivity.findViewById(R.id.capture);
        mViewToCapture = mActivity.findViewById(R.id.capture);
    }

    @Test
    @SmallTest
    public void testCreateSnapshot() {
    public void testCreateSnapshot_software() {
        assertChildrenVisibility();
        testCreateSnapshot(true, R.drawable.view_capture_test_no_children_golden);
        testCreateSnapshot(new SoftwareCanvasProvider(), true,
                R.drawable.view_capture_test_no_children_golden);
        assertChildrenVisibility();
        testCreateSnapshot(false, R.drawable.view_capture_test_with_children_golden);
        testCreateSnapshot(new SoftwareCanvasProvider(), false,
                R.drawable.view_capture_test_with_children_golden);
        assertChildrenVisibility();
    }

    private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
        Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
    @Test
    @SmallTest
    public void testCreateSnapshot_hardware() {
        Assume.assumeTrue(mViewToCapture.isHardwareAccelerated());
        assertChildrenVisibility();
        testCreateSnapshot(new HardwareCanvasProvider(), true,
                R.drawable.view_capture_test_no_children_golden);
        assertChildrenVisibility();
        testCreateSnapshot(new HardwareCanvasProvider(), false,
                R.drawable.view_capture_test_with_children_golden);
        assertChildrenVisibility();
    }

    private void testCreateSnapshot(
            CanvasProvider canvasProvider, boolean skipChildren, int goldenResId) {
        Bitmap result = mViewToCapture.createSnapshot(canvasProvider, skipChildren);
        result.setHasAlpha(false); // resource will have no alpha, since content is opaque
        Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);

        // We dont care about the config of the bitmap, so convert to same config before comparing
        result = result.copy(golden.getConfig(), false);
        assertTrue(golden.sameAs(result));
    }