Add Gainmap bitmap & imagedecoder

Bug: 266628247
Test: builds & boots
Change-Id: I0da44e0c48cf8a6b6f95e3b62f6d5f74bd6c1eab
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
old mode 100755
new mode 100644
index c68a6b9..10c287d
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -6,6 +6,7 @@
 #include <hwui/Paint.h>
 
 #include "CreateJavaOutputStreamAdaptor.h"
+#include "Gainmap.h"
 #include "GraphicsJNI.h"
 #include "HardwareBufferHelpers.h"
 #include "SkBitmap.h"
@@ -47,6 +48,8 @@
 
 namespace android {
 
+jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap);
+
 class BitmapWrapper {
 public:
     explicit BitmapWrapper(Bitmap* bitmap)
@@ -1251,68 +1254,77 @@
     return bitmapHolder->bitmap().setImmutable();
 }
 
+static jboolean Bitmap_hasGainmap(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return false;
+
+    return bitmapHolder->bitmap().hasGainmap();
+}
+
+static jobject Bitmap_extractGainmap(JNIEnv* env, jobject, jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return nullptr;
+    if (!bitmapHolder->bitmap().hasGainmap()) return nullptr;
+
+    return Gainmap_extractFromBitmap(env, bitmapHolder->bitmap());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gBitmapMethods[] = {
-    {   "nativeCreate",             "([IIIIIIZJ)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_creator },
-    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_copy },
-    {   "nativeCopyAshmem",         "(J)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_copyAshmem },
-    {   "nativeCopyAshmemConfig",   "(JI)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_copyAshmemConfig },
-    {   "nativeGetAshmemFD",        "(J)I", (void*)Bitmap_getAshmemFd },
-    {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
-    {   "nativeRecycle",            "(J)V", (void*)Bitmap_recycle },
-    {   "nativeReconfigure",        "(JIIIZ)V", (void*)Bitmap_reconfigure },
-    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
-        (void*)Bitmap_compress },
-    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
-    {   "nativeErase",              "(JJJ)V", (void*)Bitmap_eraseLong },
-    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
-    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
-    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
-    {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
-    {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
-    {   "nativeSetPremultiplied",   "(JZ)V", (void*)Bitmap_setPremultiplied},
-    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
-    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
-    {   "nativeCreateFromParcel",
-        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_createFromParcel },
-    {   "nativeWriteToParcel",      "(JILandroid/os/Parcel;)Z",
-        (void*)Bitmap_writeToParcel },
-    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_extractAlpha },
-    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
-    {   "nativeGetPixel",           "(JII)I", (void*)Bitmap_getPixel },
-    {   "nativeGetColor",           "(JII)J", (void*)Bitmap_getColor },
-    {   "nativeGetPixels",          "(J[IIIIIII)V", (void*)Bitmap_getPixels },
-    {   "nativeSetPixel",           "(JIII)V", (void*)Bitmap_setPixel },
-    {   "nativeSetPixels",          "(J[IIIIIII)V", (void*)Bitmap_setPixels },
-    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
-                                            (void*)Bitmap_copyPixelsToBuffer },
-    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
-                                            (void*)Bitmap_copyPixelsFromBuffer },
-    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
-    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
-    {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
-    {   "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
-        (void*)Bitmap_copyPreserveInternalConfig },
-    {   "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
-        (void*) Bitmap_wrapHardwareBufferBitmap },
-    {   "nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
-        (void*) Bitmap_getHardwareBuffer },
-    {   "nativeComputeColorSpace",  "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
-    {   "nativeSetColorSpace",      "(JJ)V", (void*)Bitmap_setColorSpace },
-    {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
-    {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
-    {   "nativeSetImmutable",       "(J)V", (void*)Bitmap_setImmutable},
+        {"nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;", (void*)Bitmap_creator},
+        {"nativeCopy", "(JIZ)Landroid/graphics/Bitmap;", (void*)Bitmap_copy},
+        {"nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmem},
+        {"nativeCopyAshmemConfig", "(JI)Landroid/graphics/Bitmap;", (void*)Bitmap_copyAshmemConfig},
+        {"nativeGetAshmemFD", "(J)I", (void*)Bitmap_getAshmemFd},
+        {"nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer},
+        {"nativeRecycle", "(J)V", (void*)Bitmap_recycle},
+        {"nativeReconfigure", "(JIIIZ)V", (void*)Bitmap_reconfigure},
+        {"nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress},
+        {"nativeErase", "(JI)V", (void*)Bitmap_erase},
+        {"nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong},
+        {"nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes},
+        {"nativeConfig", "(J)I", (void*)Bitmap_config},
+        {"nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha},
+        {"nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied},
+        {"nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha},
+        {"nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied},
+        {"nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap},
+        {"nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap},
+        {"nativeCreateFromParcel", "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+         (void*)Bitmap_createFromParcel},
+        {"nativeWriteToParcel", "(JILandroid/os/Parcel;)Z", (void*)Bitmap_writeToParcel},
+        {"nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;", (void*)Bitmap_extractAlpha},
+        {"nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId},
+        {"nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel},
+        {"nativeGetColor", "(JII)J", (void*)Bitmap_getColor},
+        {"nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels},
+        {"nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel},
+        {"nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels},
+        {"nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsToBuffer},
+        {"nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer},
+        {"nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs},
+        {"nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw},
+        {"nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount},
+        {"nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
+         (void*)Bitmap_copyPreserveInternalConfig},
+        {"nativeWrapHardwareBufferBitmap",
+         "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
+         (void*)Bitmap_wrapHardwareBufferBitmap},
+        {"nativeGetHardwareBuffer", "(J)Landroid/hardware/HardwareBuffer;",
+         (void*)Bitmap_getHardwareBuffer},
+        {"nativeComputeColorSpace", "(J)Landroid/graphics/ColorSpace;",
+         (void*)Bitmap_computeColorSpace},
+        {"nativeSetColorSpace", "(JJ)V", (void*)Bitmap_setColorSpace},
+        {"nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB},
+        {"nativeIsSRGBLinear", "(J)Z", (void*)Bitmap_isSRGBLinear},
+        {"nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable},
+        {"nativeExtractGainmap", "(J)Landroid/graphics/Gainmap;", (void*)Bitmap_extractGainmap},
 
-    // ------------ @CriticalNative ----------------
-    {   "nativeIsImmutable",        "(J)Z", (void*)Bitmap_isImmutable},
-    {   "nativeIsBackedByAshmem",   "(J)Z", (void*)Bitmap_isBackedByAshmem}
+        // ------------ @CriticalNative ----------------
+        {"nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable},
+        {"nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem},
+        {"nativeHasGainmap", "(J)Z", (void*)Bitmap_hasGainmap},
 
 };
 
diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp
new file mode 100644
index 0000000..f2efbc7
--- /dev/null
+++ b/libs/hwui/jni/Gainmap.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Gainmap.h>
+
+#include "Bitmap.h"
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+namespace android {
+
+static jclass gGainmap_class;
+static jmethodID gGainmap_constructorMethodID;
+
+using namespace uirenderer;
+
+static Gainmap* fromJava(jlong gainmap) {
+    return reinterpret_cast<Gainmap*>(gainmap);
+}
+
+static int getCreateFlags(const sk_sp<Bitmap>& bitmap) {
+    int flags = 0;
+    if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
+        flags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
+    }
+    if (!bitmap->isImmutable()) {
+        flags |= android::bitmap::kBitmapCreateFlag_Mutable;
+    }
+    return flags;
+}
+
+jobject Gainmap_extractFromBitmap(JNIEnv* env, const Bitmap& bitmap) {
+    auto gainmap = bitmap.gainmap();
+    jobject jGainmapImage;
+    size_t allocationSize;
+
+    {
+        // Scope to guard the release of nativeBitmap
+        auto nativeBitmap = gainmap->bitmap;
+        const int createFlags = getCreateFlags(nativeBitmap);
+        allocationSize = nativeBitmap->getAllocationByteCount();
+        jGainmapImage = bitmap::createBitmap(env, nativeBitmap.release(), createFlags);
+    }
+
+    // Grab a ref for the jobject
+    gainmap->incStrong(0);
+    jobject obj = env->NewObject(gGainmap_class, gGainmap_constructorMethodID, jGainmapImage,
+                                 gainmap.get(), allocationSize + sizeof(Gainmap), true);
+
+    if (env->ExceptionCheck() != 0) {
+        // sadtrombone
+        gainmap->decStrong(0);
+        ALOGE("*** Uncaught exception returned from Java call!\n");
+        env->ExceptionDescribe();
+    }
+    return obj;
+}
+
+static void Gainmap_destructor(Gainmap* gainmap) {
+    gainmap->decStrong(0);
+}
+
+static jlong Gainmap_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Gainmap_destructor));
+}
+
+static void Gainmap_setGainmapMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat r, jfloat g,
+                                  jfloat b) {
+    fromJava(gainmapPtr)->info.fLogRatioMax = {r, g, b, 1.f};
+}
+
+static void Gainmap_getGainmapMax(JNIEnv* env, jobject, jlong gainmapPtr, jfloatArray components) {
+    const auto ratioMax = fromJava(gainmapPtr)->info.fLogRatioMax;
+    jfloat buf[3]{ratioMax.fR, ratioMax.fG, ratioMax.fB};
+    env->SetFloatArrayRegion(components, 0, 3, buf);
+}
+
+static void Gainmap_setHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr, jfloat max) {
+    fromJava(gainmapPtr)->info.fHdrRatioMax = max;
+}
+
+static jfloat Gainmap_getHdrRatioMax(JNIEnv*, jobject, jlong gainmapPtr) {
+    return fromJava(gainmapPtr)->info.fHdrRatioMax;
+}
+
+static void Gainmap_setHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr, jfloat min) {
+    fromJava(gainmapPtr)->info.fHdrRatioMin = min;
+}
+
+static jfloat Gainmap_getHdrRatioMin(JNIEnv*, jobject, jlong gainmapPtr) {
+    return fromJava(gainmapPtr)->info.fHdrRatioMin;
+}
+
+static const JNINativeMethod gGainmapMethods[] = {
+        {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer},
+        {"nSetGainmapMax", "(JFFF)V", (void*)Gainmap_setGainmapMax},
+        {"nGetGainmapMax", "(J[F)V", (void*)Gainmap_getGainmapMax},
+        {"nSetHdrRatioMax", "(JF)V", (void*)Gainmap_setHdrRatioMax},
+        {"nGetHdrRatioMax", "(J)F", (void*)Gainmap_getHdrRatioMax},
+        {"nSetHdrRatioMin", "(JF)V", (void*)Gainmap_setHdrRatioMin},
+        {"nGetHdrRatioMin", "(J)F", (void*)Gainmap_getHdrRatioMin},
+};
+
+int register_android_graphics_Gainmap(JNIEnv* env) {
+    gGainmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Gainmap"));
+    gGainmap_constructorMethodID =
+            GetMethodIDOrDie(env, gGainmap_class, "<init>", "(Landroid/graphics/Bitmap;JIZ)V");
+    return android::RegisterMethodsOrDie(env, "android/graphics/Gainmap", gGainmapMethods,
+                                         NELEM(gGainmapMethods));
+}
+
+}  // namespace android
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index bad710d..add62b1 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -14,20 +14,10 @@
  * limitations under the License.
  */
 
-#include "Bitmap.h"
-#include "BitmapFactory.h"
-#include "ByteBufferStreamAdaptor.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-#include "GraphicsJNI.h"
 #include "ImageDecoder.h"
-#include "NinePatchPeeker.h"
-#include "Utils.h"
-
-#include <hwui/Bitmap.h>
-#include <hwui/ImageDecoder.h>
-#include <HardwareBitmapUploader.h>
 
 #include <FrontBufferedStream.h>
+#include <HardwareBitmapUploader.h>
 #include <SkAndroidCodec.h>
 #include <SkBitmap.h>
 #include <SkColorSpace.h>
@@ -35,11 +25,22 @@
 #include <SkRect.h>
 #include <SkStream.h>
 #include <SkString.h>
-
 #include <androidfw/Asset.h>
 #include <fcntl.h>
+#include <gui/TraceUtils.h>
+#include <hwui/Bitmap.h>
+#include <hwui/ImageDecoder.h>
 #include <sys/stat.h>
 
+#include "Bitmap.h"
+#include "BitmapFactory.h"
+#include "ByteBufferStreamAdaptor.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Gainmap.h"
+#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
+#include "Utils.h"
+
 using namespace android;
 
 static jclass    gImageDecoder_class;
@@ -246,6 +247,7 @@
                                           jboolean requireUnpremul, jboolean preferRamOverQuality,
                                           jboolean asAlphaMask, jlong colorSpaceHandle,
                                           jboolean extended) {
+    ATRACE_CALL();
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
     if (!decoder->setTargetSize(targetWidth, targetHeight)) {
         doThrowISE(env, "Could not scale to target size!");
@@ -336,10 +338,21 @@
         return nullptr;
     }
 
+    ATRACE_FORMAT("Decoding %dx%d bitmap", bitmapInfo.width(), bitmapInfo.height());
     SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
     jthrowable jexception = get_and_clear_exception(env);
-    int onPartialImageError = jexception ? kSourceException
-                                         : 0; // No error.
+    int onPartialImageError = jexception ? kSourceException : 0;  // No error.
+
+    // Only attempt to extract the gainmap if we're not post-processing, as we can't automatically
+    // mimic that to the gainmap and expect it to be meaningful. And also don't extract the gainmap
+    // if we're prioritizing RAM over quality, since the gainmap improves quality at the
+    // cost of RAM
+    if (result == SkCodec::kSuccess && !jpostProcess && !preferRamOverQuality) {
+        // The gainmap costs RAM to improve quality, so skip this if we're prioritizing RAM instead
+        result = decoder->extractGainmap(nativeBitmap.get());
+        jexception = get_and_clear_exception(env);
+    }
+
     switch (result) {
         case SkCodec::kSuccess:
             // Ignore the exception, since the decode was successful anyway.
@@ -450,6 +463,10 @@
             sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
             if (hwBitmap) {
                 hwBitmap->setImmutable();
+                if (nativeBitmap->hasGainmap()) {
+                    // TODO: Also convert to a HW gainmap image
+                    hwBitmap->setGainmap(nativeBitmap->gainmap());
+                }
                 return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
                                             ninePatchChunk, ninePatchInsets);
             }