diff options
author | 2023-02-04 00:41:47 +0000 | |
---|---|---|
committer | 2023-02-06 23:27:05 +0000 | |
commit | 45b0a0fca5ff141472af20c11f65d9ad74986b3e (patch) | |
tree | fa9c28ea5bf78b5464017197bf5253d70bbe7192 | |
parent | 51d58191e6f91e5779abb103d64c6e044ffea1ef (diff) |
Gainmap: Implement gainmap support in BitmapFactory
Test: atest BitmapFactoryTest
Bug: 267215643
Change-Id: Icbcb37e5b3a8926edc8f64d2cb9647b46ced39f7
-rw-r--r-- | libs/hwui/jni/BitmapFactory.cpp | 164 |
1 files changed, 151 insertions, 13 deletions
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index 0d5995aebbe2..571ab833f053 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -2,6 +2,20 @@ #define LOG_TAG "BitmapFactory" #include "BitmapFactory.h" + +#include <Gainmap.h> +#include <HardwareBitmapUploader.h> +#include <androidfw/Asset.h> +#include <androidfw/ResourceTypes.h> +#include <cutils/compiler.h> +#include <fcntl.h> +#include <nativehelper/JNIPlatformHelp.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/stat.h> + +#include <memory> + #include "CreateJavaOutputStreamAdaptor.h" #include "FrontBufferedStream.h" #include "GraphicsJNI.h" @@ -13,6 +27,7 @@ #include "SkCanvas.h" #include "SkColorSpace.h" #include "SkEncodedImageFormat.h" +#include "SkGainmapInfo.h" #include "SkImageInfo.h" #include "SkPaint.h" #include "SkPixelRef.h" @@ -24,17 +39,6 @@ #include "SkString.h" #include "Utils.h" -#include <HardwareBitmapUploader.h> -#include <nativehelper/JNIPlatformHelp.h> -#include <androidfw/Asset.h> -#include <androidfw/ResourceTypes.h> -#include <cutils/compiler.h> -#include <fcntl.h> -#include <memory> -#include <stdio.h> -#include <stdint.h> -#include <sys/stat.h> - jfieldID gOptions_justBoundsFieldID; jfieldID gOptions_sampleSizeFieldID; jfieldID gOptions_configFieldID; @@ -182,6 +186,115 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); } +static bool decodeGainmap(std::unique_ptr<SkStream> gainmapStream, const SkGainmapInfo& gainmapInfo, + sp<uirenderer::Gainmap>* outGainmap, const int sampleSize, float scale) { + std::unique_ptr<SkAndroidCodec> codec; + codec = SkAndroidCodec::MakeFromStream(std::move(gainmapStream), nullptr); + if (!codec) { + ALOGE("Can not create a codec for Gainmap."); + return false; + } + SkColorType decodeColorType = codec->computeOutputColorType(kN32_SkColorType); + sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(decodeColorType, nullptr); + + SkISize size = codec->getSampledDimensions(sampleSize); + + int scaledWidth = size.width(); + int scaledHeight = size.height(); + bool willScale = false; + + // Apply a fine scaling step if necessary. + if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize) || scale != 1.0f) { + willScale = true; + // The operation below may loose precision (integer division), but it is put this way to + // mimic main image scale calculation + scaledWidth = static_cast<int>((codec->getInfo().width() / sampleSize) * scale + 0.5f); + scaledHeight = static_cast<int>((codec->getInfo().height() / sampleSize) * scale + 0.5f); + } + + SkAlphaType alphaType = codec->computeOutputAlphaType(false); + + const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), decodeColorType, + alphaType, decodeColorSpace); + + const SkImageInfo& bitmapInfo = decodeInfo; + SkBitmap decodeBitmap; + sk_sp<Bitmap> nativeBitmap = nullptr; + + if (!decodeBitmap.setInfo(bitmapInfo)) { + ALOGE("Failed to setInfo."); + return false; + } + + if (willScale) { + if (!decodeBitmap.tryAllocPixels(nullptr)) { + ALOGE("OOM allocating gainmap pixels."); + return false; + } + } else { + nativeBitmap = android::Bitmap::allocateHeapBitmap(&decodeBitmap); + if (!nativeBitmap) { + ALOGE("OOM allocating gainmap pixels."); + return false; + } + } + + // Use SkAndroidCodec to perform the decode. + SkAndroidCodec::AndroidOptions codecOptions; + codecOptions.fZeroInitialized = SkCodec::kYes_ZeroInitialized; + codecOptions.fSampleSize = sampleSize; + SkCodec::Result result = codec->getAndroidPixels(decodeInfo, decodeBitmap.getPixels(), + decodeBitmap.rowBytes(), &codecOptions); + switch (result) { + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + break; + default: + ALOGE("Error decoding gainmap."); + return false; + } + + if (willScale) { + SkBitmap gainmapBitmap; + const float scaleX = scaledWidth / float(decodeBitmap.width()); + const float scaleY = scaledHeight / float(decodeBitmap.height()); + + SkColorType scaledColorType = decodeBitmap.colorType(); + gainmapBitmap.setInfo( + bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType)); + + nativeBitmap = android::Bitmap::allocateHeapBitmap(&gainmapBitmap); + if (!nativeBitmap) { + ALOGE("OOM allocating gainmap pixels."); + return false; + } + + SkPaint paint; + // kSrc_Mode instructs us to overwrite the uninitialized pixels in + // outputBitmap. Otherwise we would blend by default, which is not + // what we want. + paint.setBlendMode(SkBlendMode::kSrc); + + SkCanvas canvas(gainmapBitmap, SkCanvas::ColorBehavior::kLegacy); + canvas.scale(scaleX, scaleY); + decodeBitmap.setImmutable(); // so .asImage() doesn't make a copy + canvas.drawImage(decodeBitmap.asImage(), 0.0f, 0.0f, + SkSamplingOptions(SkFilterMode::kLinear), &paint); + } + + auto gainmap = sp<uirenderer::Gainmap>::make(); + if (!gainmap) { + ALOGE("OOM allocating Gainmap"); + return false; + } + + gainmap->info = gainmapInfo; + gainmap->bitmap = std::move(nativeBitmap); + *outGainmap = std::move(gainmap); + + return true; +} + static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { @@ -485,6 +598,19 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, return nullObjectReturn("Got null SkPixelRef"); } + bool hasGainmap = false; + SkGainmapInfo gainmapInfo; + std::unique_ptr<SkStream> gainmapStream = nullptr; + sp<uirenderer::Gainmap> gainmap = nullptr; + if (result == SkCodec::kSuccess) { + hasGainmap = codec->getAndroidGainmap(&gainmapInfo, &gainmapStream); + } + + if (hasGainmap) { + hasGainmap = + decodeGainmap(std::move(gainmapStream), gainmapInfo, &gainmap, sampleSize, scale); + } + if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) outputBitmap.setImmutable(); @@ -492,6 +618,9 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, bool isPremultiplied = !requireUnpremultiplied; if (javaBitmap != nullptr) { + if (hasGainmap) { + reuseBitmap->setGainmap(std::move(gainmap)); + } bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); outputBitmap.notifyPixelsChanged(); // If a java bitmap was passed in for reuse, pass it back @@ -507,13 +636,22 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, if (!hardwareBitmap.get()) { return nullObjectReturn("Failed to allocate a hardware bitmap"); } + if (hasGainmap) { + hardwareBitmap->setGainmap(std::move(gainmap)); + } + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); } + Bitmap* heapBitmap = defaultAllocator.getStorageObjAndReset(); + if (hasGainmap && heapBitmap != nullptr) { + heapBitmap->setGainmap(std::move(gainmap)); + } + // now create the java bitmap - return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(), - bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); + return bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags, ninePatchChunk, ninePatchInsets, + -1); } static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, |