Add HardwareBuffer support to Bitmaps
This adds a new public constructor to Bitmap to allow passing in
a HardwareBuffer to back the Bitmap. Currently, the format of the
HardwareBuffer must be RGBA_8888.
One-Pager: https://docs.google.com/document/d/1n72X-gJZhjeajp4FIQwsGPKFiHBSeWYjC6fkXYe_bS0/edit?usp=sharing
Bug: 116713113
Test: manual - ran CTS tests
Change-Id: Ic437825138eafea526326e6cf300f116867ba29e
diff --git a/api/current.txt b/api/current.txt
index 2753085..5a2d4cc 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -13252,6 +13252,7 @@
method public void setPixels(int[], int, int, int, int, int, int);
method public void setPremultiplied(boolean);
method public void setWidth(int);
+ method public static android.graphics.Bitmap wrapHardwareBuffer(android.hardware.HardwareBuffer, android.graphics.ColorSpace);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.Bitmap> CREATOR;
field public static final int DENSITY_NONE = 0; // 0x0
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 897f6fa5..fafb1cd 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -27,6 +27,10 @@
#include <hwui/Bitmap.h>
#include <renderthread/RenderProxy.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+
#include "core_jni_helpers.h"
#include <jni.h>
@@ -1113,8 +1117,7 @@
static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
- // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
- // format and SRGB color space.
+ // Bitmap::createFrom currently assumes SRGB color space for RGBA images.
// To support any color space, we need to pass an additional ColorSpace argument to
// java Bitmap.createHardwareBitmap.
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
@@ -1125,6 +1128,22 @@
return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
}
+static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
+ jfloatArray xyzD50, jobject transferParameters) {
+ SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+ SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
+ AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
+ hardwareBuffer);
+ sp<GraphicBuffer> buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf));
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, colorSpace);
+ if (!bitmap.get()) {
+ ALOGW("failed to create hardware bitmap from hardware buffer");
+ return NULL;
+ }
+ return bitmap::createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
+}
+
static jobject Bitmap_createGraphicBufferHandle(JNIEnv* env, jobject, jlong bitmapPtr) {
LocalScopedBitmap bitmapHandle(bitmapPtr);
LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
@@ -1204,6 +1223,8 @@
(void*)Bitmap_copyPreserveInternalConfig },
{ "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;",
(void*) Bitmap_createHardwareBitmap },
+ { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
+ (void*) Bitmap_wrapHardwareBufferBitmap },
{ "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
(void*) Bitmap_createGraphicBufferHandle },
{ "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace },
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 9cbbf4e..1cd756f 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -24,6 +24,7 @@
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
+import android.hardware.HardwareBuffer;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
@@ -724,6 +725,48 @@
}
/**
+ * Create a hardware bitmap backed by a {@link HardwareBuffer}.
+ *
+ * <p>The passed HardwareBuffer's usage flags must contain
+ * {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}.
+ *
+ * <p>The bitmap will keep a reference to the buffer so that callers can safely close the
+ * HardwareBuffer without affecting the Bitmap. However the HardwareBuffer must not be
+ * modified while a wrapped Bitmap is accessing it. Doing so will result in undefined behavior.
+ *
+ * @param hardwareBuffer The HardwareBuffer to wrap.
+ * @param colorSpace The color space of the bitmap. Must be a {@link ColorSpace.Rgb} colorspace.
+ * If null, SRGB is assumed.
+ * @return A bitmap wrapping the buffer, or null if there was a problem creating the bitmap.
+ * @throws IllegalArgumentException if the HardwareBuffer has an invalid usage, or an invalid
+ * colorspace is given.
+ */
+ @Nullable
+ public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer,
+ @Nullable ColorSpace colorSpace) {
+ if ((hardwareBuffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
+ }
+ int format = hardwareBuffer.getFormat();
+ ColorSpace.Rgb rgb = null;
+ if (colorSpace != null) {
+ if (!(colorSpace instanceof ColorSpace.Rgb)) {
+ throw new IllegalArgumentException("colorSpace must be an RGB color space");
+ }
+ rgb = (ColorSpace.Rgb) colorSpace;
+ } else {
+ rgb = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
+ }
+ ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters();
+ if (parameters == null) {
+ throw new IllegalArgumentException("colorSpace must use an ICC "
+ + "parametric transfer function");
+ }
+ ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
+ return nativeWrapHardwareBufferBitmap(hardwareBuffer, d50.getTransform(), parameters);
+ }
+
+ /**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
* the source bitmap, the source bitmap is returned and no new bitmap is
@@ -2118,6 +2161,9 @@
private static native int nativeGetAllocationByteCount(long nativeBitmap);
private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer);
+ private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
+ @Size(9) float[] xyzD50,
+ ColorSpace.Rgb.TransferParameters p);
private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
private static native boolean nativeIsSRGB(long nativePtr);
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 9c28453..b04194f 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -134,14 +134,15 @@
}
sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
- PixelFormat format = graphicBuffer->getPixelFormat();
- if (!graphicBuffer.get() ||
- (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
- return nullptr;
- }
+ return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
+ // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
+ // view the colorspace as RGBA8888.
SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
- SkColorSpace::MakeSRGB());
+ colorSpace);
return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 43d457a..238c764 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -64,6 +64,8 @@
size_t rowBytes);
static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+ static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
+ sk_sp<SkColorSpace> colorSpace);
static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);