From b2e9f5244b5a78423ba33c364bb658e43851db21 Mon Sep 17 00:00:00 2001 From: renn Date: Wed, 26 Sep 2018 10:49:00 -0700 Subject: [PATCH] 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 --- api/current.txt | 1 + core/jni/android/graphics/Bitmap.cpp | 25 +++++++++++- graphics/java/android/graphics/Bitmap.java | 46 ++++++++++++++++++++++ libs/hwui/hwui/Bitmap.cpp | 13 +++--- libs/hwui/hwui/Bitmap.h | 2 + 5 files changed, 79 insertions(+), 8 deletions(-) diff --git a/api/current.txt b/api/current.txt index 2753085f52694..5a2d4cc11fa67 100755 --- a/api/current.txt +++ b/api/current.txt @@ -13252,6 +13252,7 @@ package android.graphics { 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 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 897f6fa537747..fafb1cd5d44c3 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -27,6 +27,10 @@ #include #include +#include + +#include + #include "core_jni_helpers.h" #include @@ -1113,8 +1117,7 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) { sp 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::createFrom(buffer); @@ -1125,6 +1128,22 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic 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 colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix); + AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, + hardwareBuffer); + sp buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf)); + sk_sp 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 @@ static const JNINativeMethod gBitmapMethods[] = { (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 9cbbf4ef366fc..1cd756f9f73f8 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -24,6 +24,7 @@ import android.annotation.Size; 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; @@ -723,6 +724,48 @@ public final class Bitmap implements Parcelable { return nativeCreateHardwareBitmap(graphicBuffer); } + /** + * Create a hardware bitmap backed by a {@link HardwareBuffer}. + * + *

The passed HardwareBuffer's usage flags must contain + * {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}. + * + *

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 @@ -2118,6 +2161,9 @@ public final class Bitmap implements Parcelable { 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 9c284531674f5..b04194f378bc5 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -134,14 +134,15 @@ sk_sp Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) } sk_sp Bitmap::createFrom(sp 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::createFrom(sp graphicBuffer, sk_sp 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(new Bitmap(graphicBuffer.get(), info)); } diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 43d457a77b15b..238c764cdea60 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -64,6 +64,8 @@ public: size_t rowBytes); static sk_sp createFrom(sp graphicBuffer); + static sk_sp createFrom(sp graphicBuffer, + sk_sp colorSpace); static sk_sp createFrom(const SkImageInfo&, SkPixelRef&);