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

Commit 716f3817 authored by Chris Craik's avatar Chris Craik Committed by John Reck
Browse files

Support recording HW Bitmaps in Picture

Bug: 34881007
Test: bit CtsGraphicsTestCases:*
Test: bit CtsUiRenderingTestCases:.testclasses.HardwareBitmapTests

Change-Id: Ic751c356682ea3db17a1b031ec46106a1a2ab918
parent e53c1a1b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -12987,6 +12987,8 @@ package android.graphics {
    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
    method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
    method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
    method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
    method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
    method public int describeContents();
    method public void eraseColor(int);
@@ -14043,6 +14045,7 @@ package android.graphics {
    method public void endRecording();
    method public int getHeight();
    method public int getWidth();
    method public boolean requiresHardwareAcceleration();
    method public deprecated void writeToStream(java.io.OutputStream);
  }
+12 −3
Original line number Diff line number Diff line
@@ -526,10 +526,19 @@ public abstract class BaseCanvas {
        return mAllowHwBitmapsInSwMode;
    }

    /**
     * @hide
     */
    protected void onHwBitmapInSwMode() {
        if (!mAllowHwBitmapsInSwMode) {
            throw new IllegalArgumentException(
                    "Software rendering doesn't support hardware bitmaps");
        }
    }

    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
        if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
                && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
            throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
            onHwBitmapInSwMode();
        }
    }

+86 −0
Original line number Diff line number Diff line
@@ -29,6 +29,10 @@ import android.os.StrictMode;
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.DisplayListCanvas;
import android.view.RenderNode;
import android.view.ThreadedRenderer;

import libcore.util.NativeAllocationRegistry;

import java.io.OutputStream;
@@ -1170,6 +1174,82 @@ public final class Bitmap implements Parcelable {
        return createBitmap(display, colors, 0, width, width, height, config);
    }

    /**
     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
     *
     * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
     * width and height the same as the Picture's width and height and a Config.HARDWARE
     * config.
     *
     * @param source The recorded {@link Picture} of drawing commands that will be
     *               drawn into the returned Bitmap.
     * @return An immutable bitmap with a HARDWARE config whose contents are created
     * from the recorded drawing commands in the Picture source.
     */
    public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
        return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
    }

    /**
     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
     *
     * The bitmap will be immutable with the given width and height. If the width and height
     * are not the same as the Picture's width & height, the Picture will be scaled to
     * fit the given width and height.
     *
     * @param source The recorded {@link Picture} of drawing commands that will be
     *               drawn into the returned Bitmap.
     * @param width The width of the bitmap to create. The picture's width will be
     *              scaled to match if necessary.
     * @param height The height of the bitmap to create. The picture's height will be
     *              scaled to match if necessary.
     * @param config The {@link Config} of the created bitmap. If this is null then
     *               the bitmap will be {@link Config#HARDWARE}.
     *
     * @return An immutable bitmap with a HARDWARE config whose contents are created
     * from the recorded drawing commands in the Picture source.
     */
    public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
            @NonNull Config config) {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("width & height must be > 0");
        }
        if (config == null) {
            throw new IllegalArgumentException("Config must not be null");
        }
        if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
            StrictMode.noteSlowCall("GPU readback");
        }
        if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
            final RenderNode node = RenderNode.create("BitmapTemporary", null);
            node.setLeftTopRightBottom(0, 0, width, height);
            node.setClipToBounds(false);
            final DisplayListCanvas canvas = node.start(width, height);
            if (source.getWidth() != width || source.getHeight() != height) {
                canvas.scale(width / (float) source.getWidth(),
                        height / (float) source.getHeight());
            }
            canvas.drawPicture(source);
            node.end(canvas);
            Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
            if (config != Config.HARDWARE) {
                bitmap = bitmap.copy(config, false);
            }
            return bitmap;
        } else {
            Bitmap bitmap = Bitmap.createBitmap(width, height, config);
            Canvas canvas = new Canvas(bitmap);
            if (source.getWidth() != width || source.getHeight() != height) {
                canvas.scale(width / (float) source.getWidth(),
                        height / (float) source.getHeight());
            }
            canvas.drawPicture(source);
            canvas.setBitmap(null);
            bitmap.makeImmutable();
            return bitmap;
        }
    }

    /**
     * Returns an optional array of private data, used by the UI system for
     * some bitmaps. Not intended to be called by applications.
@@ -1259,6 +1339,12 @@ public final class Bitmap implements Parcelable {
        return mIsMutable;
    }

    /** @hide */
    public final void makeImmutable() {
        // todo mIsMutable = false;
        // todo nMakeImmutable();
    }

    /**
     * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
     * When a pixel is pre-multiplied, the RGB components have been multiplied by
+32 −6
Original line number Diff line number Diff line
@@ -31,8 +31,9 @@ import java.io.OutputStream;
 * be replayed on a hardware accelerated canvas.</p>
 */
public class Picture {
    private Canvas mRecordingCanvas;
    private PictureCanvas mRecordingCanvas;
    private long mNativePicture;
    private boolean mRequiresHwAcceleration;

    private static final int WORKING_STREAM_STORAGE = 16 * 1024;

@@ -78,8 +79,12 @@ public class Picture {
     * into it.
     */
    public Canvas beginRecording(int width, int height) {
        if (mRecordingCanvas != null) {
            throw new IllegalStateException("Picture already recording, must call #endRecording()");
        }
        long ni = nativeBeginRecording(mNativePicture, width, height);
        mRecordingCanvas = new RecordingCanvas(this, ni);
        mRecordingCanvas = new PictureCanvas(this, ni);
        mRequiresHwAcceleration = false;
        return mRecordingCanvas;
    }

@@ -91,6 +96,7 @@ public class Picture {
     */
    public void endRecording() {
        if (mRecordingCanvas != null) {
            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
            mRecordingCanvas = null;
            nativeEndRecording(mNativePicture);
        }
@@ -112,6 +118,18 @@ public class Picture {
      return nativeGetHeight(mNativePicture);
    }

    /**
     * Indicates whether or not this Picture contains recorded commands that only work when
     * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
     * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
     *
     * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
     *         false otherwise.
     */
    public boolean requiresHardwareAcceleration() {
        return mRequiresHwAcceleration;
    }

    /**
     * Draw this picture on the canvas.
     * <p>
@@ -129,6 +147,9 @@ public class Picture {
        if (mRecordingCanvas != null) {
            endRecording();
        }
        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
            canvas.onHwBitmapInSwMode();
        }
        nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
    }

@@ -164,8 +185,7 @@ public class Picture {
        if (stream == null) {
            throw new NullPointerException();
        }
        if (!nativeWriteToStream(mNativePicture, stream,
                             new byte[WORKING_STREAM_STORAGE])) {
        if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
            throw new RuntimeException();
        }
    }
@@ -182,10 +202,11 @@ public class Picture {
                                           OutputStream stream, byte[] storage);
    private static native void nativeDestructor(long nativePicture);

    private static class RecordingCanvas extends Canvas {
    private static class PictureCanvas extends Canvas {
        private final Picture mPicture;
        boolean mHoldsHwBitmap;

        public RecordingCanvas(Picture pict, long nativeCanvas) {
        public PictureCanvas(Picture pict, long nativeCanvas) {
            super(nativeCanvas);
            mPicture = pict;
        }
@@ -202,5 +223,10 @@ public class Picture {
            }
            super.drawPicture(picture);
        }

        @Override
        protected void onHwBitmapInSwMode() {
            mHoldsHwBitmap = true;
        }
    }
}