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

Commit 81f6fa18 authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Support recording HW Bitmaps in Picture"

parents aa2c46cc 716f3817
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -13048,6 +13048,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);
@@ -14105,6 +14107,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
@@ -541,10 +541,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;
        }
    }
}