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

Commit b2e9f524 authored by renn's avatar renn Committed by John Reck
Browse files

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
parent 73ff9c52
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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<android.graphics.Bitmap> CREATOR;
    field public static final int DENSITY_NONE = 0; // 0x0
+23 −2
Original line number Diff line number Diff line
@@ -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_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit

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 @@ 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<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 @@ 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 },
+46 −0
Original line number Diff line number Diff line
@@ -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}.
     *
     * <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
@@ -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);
+7 −6
Original line number Diff line number Diff line
@@ -134,14 +134,15 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef)
}

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));
}

+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ public:
                                              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&);