Support recording HW Bitmaps in Picture
Bug: 34881007 Test: bit CtsGraphicsTestCases:* Test: bit CtsUiRenderingTestCases:.testclasses.HardwareBitmapTests Change-Id: Ic751c356682ea3db17a1b031ec46106a1a2ab918
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user