From b0da4a5d0dbcd8ab5ef06139cc4717deadd96952 Mon Sep 17 00:00:00 2001 From: Fyodor Kyslov Date: Thu, 26 Jan 2023 18:39:33 +0000 Subject: Implementing Gainmap support on BRD Test: atest BitmapRegionDecoderTest Change-Id: Ia6285b5adf6b5484bf38063ffffbae103d36726e --- libs/hwui/jni/BitmapRegionDecoder.cpp | 159 +++++++++++++++++++++++++++++++--- libs/hwui/jni/Graphics.cpp | 3 +- libs/hwui/jni/GraphicsJNI.h | 6 +- 3 files changed, 148 insertions(+), 20 deletions(-) diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index eb56ae310231..f93be038cc36 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -17,28 +17,139 @@ #undef LOG_TAG #define LOG_TAG "BitmapRegionDecoder" +#include "BitmapRegionDecoder.h" + +#include +#include +#include + +#include + #include "BitmapFactory.h" #include "CreateJavaOutputStreamAdaptor.h" +#include "Gainmap.h" #include "GraphicsJNI.h" -#include "Utils.h" - -#include "BitmapRegionDecoder.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" -#include -#include -#include +using namespace android; -#include +namespace android { +class BitmapRegionDecoderWrapper { +public: + static std::unique_ptr Make(sk_sp data) { + std::unique_ptr mainImageBRD = + skia::BitmapRegionDecoder::Make(std::move(data)); + if (!mainImageBRD) { + return nullptr; + } -using namespace android; + SkGainmapInfo gainmapInfo; + std::unique_ptr gainmapStream; + std::unique_ptr gainmapBRD = nullptr; + if (mainImageBRD->getAndroidGainmap(&gainmapInfo, &gainmapStream)) { + sk_sp 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( + 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 computeOutputColorSpace(SkColorType outputColorType, + sk_sp 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 prefColorSpace) { + return mMainImageBRD->decodeRegion(bitmap, allocator, desiredSubset, sampleSize, colorType, + requireUnpremul, prefColorSpace); + } + + bool decodeGainmapRegion(sp* outGainmap, const SkIRect& desiredSubset, + int sampleSize, bool requireUnpremul) { + SkColorType decodeColorType = mGainmapBRD->computeOutputColorType(kN32_SkColorType); + sk_sp 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 nativeBitmap(heapAlloc.getStorageObjAndReset()); + if (!nativeBitmap) { + ALOGE("OOM allocating Bitmap for Gainmap"); + return false; + } + auto gainmap = sp::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 mainImageBRD, + std::unique_ptr gainmapBRD, + SkGainmapInfo info, std::unique_ptr stream) + : mMainImageBRD(std::move(mainImageBRD)) + , mGainmapBRD(std::move(gainmapBRD)) + , mGainmapInfo(info) + , mGainmapStream(std::move(stream)) {} + + std::unique_ptr mMainImageBRD; + std::unique_ptr mGainmapBRD; + SkGainmapInfo mGainmapInfo; + std::unique_ptr mGainmapStream; +}; +} // namespace android static jobject createBitmapRegionDecoder(JNIEnv* env, sk_sp 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 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in recycledBytes = recycledBitmap->getAllocationByteCount(); } - auto* brd = reinterpret_cast(brdHandle); + auto* brd = reinterpret_cast(brdHandle); SkColorType decodeColorType = brd->computeOutputColorType(colorType); if (isHardware) { @@ -196,9 +307,22 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); } + sp 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 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in } if (isHardware) { sk_sp 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(brdHandle); + auto* brd = reinterpret_cast(brdHandle); return static_cast(brd->height()); } static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { - auto* brd = reinterpret_cast(brdHandle); + auto* brd = reinterpret_cast(brdHandle); return static_cast(brd->width()); } static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { - auto* brd = reinterpret_cast(brdHandle); + auto* brd = reinterpret_cast(brdHandle); delete brd; } diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index f5cd793e41c8..28d78b4474f7 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -516,8 +516,7 @@ int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetri /////////////////////////////////////////////////////////////////////////////////////////// -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 c4a61ccd1e5f..6b983c10bdca 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -20,9 +20,7 @@ class SkCanvas; struct SkFontMetrics; namespace android { -namespace skia { - class BitmapRegionDecoder; -} +class BitmapRegionDecoderWrapper; class Canvas; class Paint; struct Typeface; @@ -119,7 +117,7 @@ public: 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 -- cgit v1.2.3-59-g8ed1b