Implementing Gainmap support on BRD

Test: atest BitmapRegionDecoderTest

Change-Id: Ia6285b5adf6b5484bf38063ffffbae103d36726e
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index eb56ae3..f93be03 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -17,17 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "BitmapRegionDecoder"
 
-#include "BitmapFactory.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-#include "GraphicsJNI.h"
-#include "Utils.h"
-
 #include "BitmapRegionDecoder.h"
-#include "SkBitmap.h"
-#include "SkCodec.h"
-#include "SkColorSpace.h"
-#include "SkData.h"
-#include "SkStream.h"
 
 #include <HardwareBitmapUploader.h>
 #include <androidfw/Asset.h>
@@ -35,10 +25,131 @@
 
 #include <memory>
 
+#include "BitmapFactory.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Gainmap.h"
+#include "GraphicsJNI.h"
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkColorSpace.h"
+#include "SkData.h"
+#include "SkGainmapInfo.h"
+#include "SkStream.h"
+#include "SkStreamPriv.h"
+#include "Utils.h"
+
 using namespace android;
 
+namespace android {
+class BitmapRegionDecoderWrapper {
+public:
+    static std::unique_ptr<BitmapRegionDecoderWrapper> Make(sk_sp<SkData> data) {
+        std::unique_ptr<skia::BitmapRegionDecoder> mainImageBRD =
+                skia::BitmapRegionDecoder::Make(std::move(data));
+        if (!mainImageBRD) {
+            return nullptr;
+        }
+
+        SkGainmapInfo gainmapInfo;
+        std::unique_ptr<SkStream> gainmapStream;
+        std::unique_ptr<skia::BitmapRegionDecoder> gainmapBRD = nullptr;
+        if (mainImageBRD->getAndroidGainmap(&gainmapInfo, &gainmapStream)) {
+            sk_sp<SkData> data = nullptr;
+            if (gainmapStream->getMemoryBase()) {
+                // It is safe to make without copy because we'll hold onto the stream.
+                data = SkData::MakeWithoutCopy(gainmapStream->getMemoryBase(),
+                                               gainmapStream->getLength());
+            } else {
+                data = SkCopyStreamToData(gainmapStream.get());
+                // We don't need to hold the stream anymore
+                gainmapStream = nullptr;
+            }
+            gainmapBRD = skia::BitmapRegionDecoder::Make(std::move(data));
+        }
+
+        return std::unique_ptr<BitmapRegionDecoderWrapper>(
+                new BitmapRegionDecoderWrapper(std::move(mainImageBRD), std::move(gainmapBRD),
+                                               gainmapInfo, std::move(gainmapStream)));
+    }
+
+    SkEncodedImageFormat getEncodedFormat() { return mMainImageBRD->getEncodedFormat(); }
+
+    SkColorType computeOutputColorType(SkColorType requestedColorType) {
+        return mMainImageBRD->computeOutputColorType(requestedColorType);
+    }
+
+    sk_sp<SkColorSpace> computeOutputColorSpace(SkColorType outputColorType,
+                                                sk_sp<SkColorSpace> prefColorSpace = nullptr) {
+        return mMainImageBRD->computeOutputColorSpace(outputColorType, prefColorSpace);
+    }
+
+    bool decodeRegion(SkBitmap* bitmap, skia::BRDAllocator* allocator, const SkIRect& desiredSubset,
+                      int sampleSize, SkColorType colorType, bool requireUnpremul,
+                      sk_sp<SkColorSpace> prefColorSpace) {
+        return mMainImageBRD->decodeRegion(bitmap, allocator, desiredSubset, sampleSize, colorType,
+                                           requireUnpremul, prefColorSpace);
+    }
+
+    bool decodeGainmapRegion(sp<uirenderer::Gainmap>* outGainmap, const SkIRect& desiredSubset,
+                             int sampleSize, bool requireUnpremul) {
+        SkColorType decodeColorType = mGainmapBRD->computeOutputColorType(kN32_SkColorType);
+        sk_sp<SkColorSpace> decodeColorSpace =
+                mGainmapBRD->computeOutputColorSpace(decodeColorType, nullptr);
+        SkBitmap bm;
+        HeapAllocator heapAlloc;
+        if (!mGainmapBRD->decodeRegion(&bm, &heapAlloc, desiredSubset, sampleSize, decodeColorType,
+                                       requireUnpremul, decodeColorSpace)) {
+            ALOGE("Error decoding Gainmap region");
+            return false;
+        }
+        sk_sp<Bitmap> nativeBitmap(heapAlloc.getStorageObjAndReset());
+        if (!nativeBitmap) {
+            ALOGE("OOM allocating Bitmap for Gainmap");
+            return false;
+        }
+        auto gainmap = sp<uirenderer::Gainmap>::make();
+        if (!gainmap) {
+            ALOGE("OOM allocating Gainmap");
+            return false;
+        }
+        gainmap->info = mGainmapInfo;
+        gainmap->bitmap = std::move(nativeBitmap);
+        *outGainmap = std::move(gainmap);
+        return true;
+    }
+
+    SkIRect calculateGainmapRegion(const SkIRect& mainImageRegion) {
+        const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width();
+        const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height();
+        // TODO: Account for rounding error?
+        return SkIRect::MakeLTRB(mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
+                                 mainImageRegion.right() * scaleX,
+                                 mainImageRegion.bottom() * scaleY);
+    }
+
+    bool hasGainmap() { return mGainmapBRD != nullptr; }
+
+    int width() const { return mMainImageBRD->width(); }
+    int height() const { return mMainImageBRD->height(); }
+
+private:
+    BitmapRegionDecoderWrapper(std::unique_ptr<skia::BitmapRegionDecoder> mainImageBRD,
+                               std::unique_ptr<skia::BitmapRegionDecoder> gainmapBRD,
+                               SkGainmapInfo info, std::unique_ptr<SkStream> stream)
+            : mMainImageBRD(std::move(mainImageBRD))
+            , mGainmapBRD(std::move(gainmapBRD))
+            , mGainmapInfo(info)
+            , mGainmapStream(std::move(stream)) {}
+
+    std::unique_ptr<skia::BitmapRegionDecoder> mMainImageBRD;
+    std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD;
+    SkGainmapInfo mGainmapInfo;
+    std::unique_ptr<SkStream> mGainmapStream;
+};
+}  // namespace android
+
 static jobject createBitmapRegionDecoder(JNIEnv* env, sk_sp<SkData> data) {
-    auto brd = skia::BitmapRegionDecoder::Make(std::move(data));
+    auto brd = android::BitmapRegionDecoderWrapper::Make(std::move(data));
     if (!brd) {
         doThrowIOE(env, "Image format not supported");
         return nullObjectReturn("CreateBitmapRegionDecoder returned null");
@@ -136,7 +247,7 @@
         recycledBytes = recycledBitmap->getAllocationByteCount();
     }
 
-    auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
+    auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
     SkColorType decodeColorType = brd->computeOutputColorType(colorType);
 
     if (isHardware) {
@@ -196,9 +307,22 @@
                 GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
     }
 
+    sp<uirenderer::Gainmap> gainmap;
+    bool hasGainmap = brd->hasGainmap();
+    if (hasGainmap) {
+        SkIRect gainmapSubset = brd->calculateGainmapRegion(subset);
+        if (!brd->decodeGainmapRegion(&gainmap, gainmapSubset, sampleSize, requireUnpremul)) {
+            // If there is an error decoding Gainmap - we don't fail. We just don't provide Gainmap
+            hasGainmap = false;
+        }
+    }
+
     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
     if (javaBitmap) {
         recycleAlloc.copyIfNecessary();
+        if (hasGainmap) {
+            recycledBitmap->setGainmap(std::move(gainmap));
+        }
         bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
         return javaBitmap;
     }
@@ -209,23 +333,30 @@
     }
     if (isHardware) {
         sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
+        if (hasGainmap) {
+            hardwareBitmap->setGainmap(std::move(gainmap));
+        }
         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
     }
-    return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
+    Bitmap* heapBitmap = heapAlloc.getStorageObjAndReset();
+    if (hasGainmap && heapBitmap != nullptr) {
+        heapBitmap->setGainmap(std::move(gainmap));
+    }
+    return android::bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags);
 }
 
 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
-    auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
+    auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
     return static_cast<jint>(brd->height());
 }
 
 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
-    auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
+    auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
     return static_cast<jint>(brd->width());
 }
 
 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
-    auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
+    auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle);
     delete brd;
 }
 
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index f5cd793..28d78b4 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -516,8 +516,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////
 
-jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, skia::BitmapRegionDecoder* bitmap)
-{
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoderWrapper* bitmap) {
     ALOG_ASSERT(bitmap != NULL);
 
     jobject obj = env->NewObject(gBitmapRegionDecoder_class,
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index c4a61cc..6b983c1 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -20,9 +20,7 @@
 struct SkFontMetrics;
 
 namespace android {
-namespace skia {
-    class BitmapRegionDecoder;
-}
+class BitmapRegionDecoderWrapper;
 class Canvas;
 class Paint;
 struct Typeface;
@@ -119,7 +117,7 @@
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
     static jobject createBitmapRegionDecoder(JNIEnv* env,
-                                             android::skia::BitmapRegionDecoder* bitmap);
+                                             android::BitmapRegionDecoderWrapper* bitmap);
 
     /**
      * Given a bitmap we natively allocate a memory block to store the contents