diff options
author | Derek Sollenberger <djsollen@google.com> | 2020-02-21 11:43:02 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-21 11:43:02 +0000 |
commit | 76e7430baead21c67fd4c81a36774a6461d2fb99 (patch) | |
tree | 239a62b59c57b4b95d8e82943bf9a48e84439819 /libs/hwui/jni/BitmapRegionDecoder.cpp | |
parent | 1ab8e44c06ca05806a81c6737cb3f6e316d8d25a (diff) | |
parent | 2173ea286afff6766043227de0bc2d82d9595f77 (diff) |
Merge changes from topic "HWUI_JNI"
* changes:
Export symbols for the newly exposed APEX/internal headers
Remove dependence on libandroid_runtime from Bitmap.cpp
Update Region.cpp to use AParcel NDK APIs
Cleanup header and build targets for libhwui clients.
Remove dependencies on headers outside UI module
Cleanup LOG_TAG when bundled in HWUI
Move android.graphics JNI & APEX files into HWUI
Diffstat (limited to 'libs/hwui/jni/BitmapRegionDecoder.cpp')
-rw-r--r-- | libs/hwui/jni/BitmapRegionDecoder.cpp | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp new file mode 100644 index 000000000000..712351382d97 --- /dev/null +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2010 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. + */ + +#undef LOG_TAG +#define LOG_TAG "BitmapRegionDecoder" + +#include "BitmapFactory.h" +#include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Utils.h" + +#include "SkBitmap.h" +#include "SkBitmapRegionDecoder.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkStream.h" + +#include <HardwareBitmapUploader.h> +#include <androidfw/Asset.h> +#include <sys/stat.h> + +#include <memory> + +using namespace android; + +static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) { + std::unique_ptr<SkBitmapRegionDecoder> brd( + SkBitmapRegionDecoder::Create(stream.release(), + SkBitmapRegionDecoder::kAndroidCodec_Strategy)); + if (!brd) { + doThrowIOE(env, "Image format not supported"); + return nullObjectReturn("CreateBitmapRegionDecoder returned null"); + } + + return GraphicsJNI::createBitmapRegionDecoder(env, brd.release()); +} + +static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, + jint offset, jint length, jboolean isShareable) { + /* If isShareable we could decide to just wrap the java array and + share it, but that means adding a globalref to the java array object + For now we just always copy the array's data if isShareable. + */ + AutoJavaByteArray ar(env, byteArray); + std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true)); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jboolean isShareable) { + NPE_CHECK_RETURN_ZERO(env, fileDescriptor); + + jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor"); + return nullObjectReturn("fstat return -1"); + } + + sk_sp<SkData> data(SkData::MakeFromFD(descriptor)); + std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data))); + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, + jobject is, // InputStream + jbyteArray storage, // byte[] + jboolean isShareable) { + jobject brd = NULL; + // for now we don't allow shareable with java inputstreams + std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage)); + + if (stream) { + // the decoder owns the stream. + brd = createBitmapRegionDecoder(env, std::move(stream)); + } + return brd; +} + +static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, + jlong native_asset, // Asset + jboolean isShareable) { + Asset* asset = reinterpret_cast<Asset*>(native_asset); + std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset)); + if (NULL == stream) { + return NULL; + } + + // the decoder owns the stream. + jobject brd = createBitmapRegionDecoder(env, std::move(stream)); + return brd; +} + +/* + * nine patch not supported + * purgeable not supported + * reportSizeToVM not supported + */ +static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, + jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { + + // Set default options. + int sampleSize = 1; + SkColorType colorType = kN32_SkColorType; + bool requireUnpremul = false; + jobject javaBitmap = nullptr; + bool isHardware = false; + sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); + // Update the default options with any options supplied by the client. + if (NULL != options) { + sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); + requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will + // ignore the values of these fields. + + // Initialize these fields to indicate a failure. If the decode succeeds, we + // will update them later on. + env->SetIntField(options, gOptions_widthFieldID, -1); + env->SetIntField(options, gOptions_heightFieldID, -1); + env->SetObjectField(options, gOptions_mimeFieldID, 0); + env->SetObjectField(options, gOptions_outConfigFieldID, 0); + env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); + } + + // Recycle a bitmap if possible. + android::Bitmap* recycledBitmap = nullptr; + size_t recycledBytes = 0; + if (javaBitmap) { + recycledBitmap = &bitmap::toBitmap(inBitmapHandle); + if (recycledBitmap->isImmutable()) { + ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); + } + recycledBytes = recycledBitmap->getAllocationByteCount(); + } + + SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + SkColorType decodeColorType = brd->computeOutputColorType(colorType); + if (decodeColorType == kRGBA_F16_SkColorType && isHardware && + !uirenderer::HardwareBitmapUploader::hasFP16Support()) { + decodeColorType = kN32_SkColorType; + } + + // Set up the pixel allocator + SkBRDAllocator* allocator = nullptr; + RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); + HeapAllocator heapAlloc; + if (javaBitmap) { + allocator = &recycleAlloc; + // We are required to match the color type of the recycled bitmap. + decodeColorType = recycledBitmap->info().colorType(); + } else { + allocator = &heapAlloc; + } + + sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace( + decodeColorType, colorSpace); + + // Decode the region. + SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); + SkBitmap bitmap; + if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, + decodeColorType, requireUnpremul, decodeColorSpace)) { + return nullObjectReturn("Failed to decode region."); + } + + // If the client provided options, indicate that the decode was successful. + if (NULL != options) { + env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); + + env->SetObjectField(options, gOptions_mimeFieldID, + getMimeTypeAsJavaString(env, brd->getEncodedFormat())); + if (env->ExceptionCheck()) { + return nullObjectReturn("OOM in encodedFormatToString()"); + } + + jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); + if (isHardware) { + configID = GraphicsJNI::kHardware_LegacyBitmapConfig; + } + jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, + gBitmapConfig_nativeToConfigMethodID, configID); + env->SetObjectField(options, gOptions_outConfigFieldID, config); + + env->SetObjectField(options, gOptions_outColorSpaceFieldID, + GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); + } + + // If we may have reused a bitmap, we need to indicate that the pixels have changed. + if (javaBitmap) { + recycleAlloc.copyIfNecessary(); + bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul); + return javaBitmap; + } + + int bitmapCreateFlags = 0; + if (!requireUnpremul) { + bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; + } + if (isHardware) { + sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap); + return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags); + } + return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags); +} + +static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->height()); +} + +static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->width()); +} + +static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + delete brd; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gBitmapRegionDecoderMethods[] = { + { "nativeDecodeRegion", + "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", + (void*)nativeDecodeRegion}, + + { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, + + { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, + + { "nativeClean", "(J)V", (void*)nativeClean}, + + { "nativeNewInstance", + "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromByteArray + }, + + { "nativeNewInstance", + "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromStream + }, + + { "nativeNewInstance", + "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromFileDescriptor + }, + + { "nativeNewInstance", + "(JZ)Landroid/graphics/BitmapRegionDecoder;", + (void*)nativeNewInstanceFromAsset + }, +}; + +int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", + gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); +} |