summaryrefslogtreecommitdiff
path: root/libs/hwui/jni/BitmapRegionDecoder.cpp
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2020-02-21 11:43:02 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-02-21 11:43:02 +0000
commit76e7430baead21c67fd4c81a36774a6461d2fb99 (patch)
tree239a62b59c57b4b95d8e82943bf9a48e84439819 /libs/hwui/jni/BitmapRegionDecoder.cpp
parent1ab8e44c06ca05806a81c6737cb3f6e316d8d25a (diff)
parent2173ea286afff6766043227de0bc2d82d9595f77 (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.cpp288
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));
+}