1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "BitmapRegionDecoder" 18 19#include "BitmapFactory.h" 20#include "CreateJavaOutputStreamAdaptor.h" 21#include "GraphicsJNI.h" 22#include "Utils.h" 23 24#include "SkBitmap.h" 25#include "SkBitmapRegionDecoder.h" 26#include "SkCodec.h" 27#include "SkData.h" 28#include "SkUtils.h" 29#include "SkPixelRef.h" 30#include "SkStream.h" 31 32#include "android_nio_utils.h" 33#include "android_util_Binder.h" 34#include "core_jni_helpers.h" 35 36#include <JNIHelp.h> 37#include <androidfw/Asset.h> 38#include <binder/Parcel.h> 39#include <jni.h> 40#include <sys/stat.h> 41 42#include <memory> 43 44using namespace android; 45 46static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) { 47 std::unique_ptr<SkBitmapRegionDecoder> brd( 48 SkBitmapRegionDecoder::Create(stream.release(), 49 SkBitmapRegionDecoder::kAndroidCodec_Strategy)); 50 if (!brd) { 51 doThrowIOE(env, "Image format not supported"); 52 return nullObjectReturn("CreateBitmapRegionDecoder returned null"); 53 } 54 55 return GraphicsJNI::createBitmapRegionDecoder(env, brd.release()); 56} 57 58static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 59 jint offset, jint length, jboolean isShareable) { 60 /* If isShareable we could decide to just wrap the java array and 61 share it, but that means adding a globalref to the java array object 62 For now we just always copy the array's data if isShareable. 63 */ 64 AutoJavaByteArray ar(env, byteArray); 65 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true)); 66 67 // the decoder owns the stream. 68 jobject brd = createBitmapRegionDecoder(env, std::move(stream)); 69 return brd; 70} 71 72static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, 73 jobject fileDescriptor, jboolean isShareable) { 74 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 75 76 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 77 78 struct stat fdStat; 79 if (fstat(descriptor, &fdStat) == -1) { 80 doThrowIOE(env, "broken file descriptor"); 81 return nullObjectReturn("fstat return -1"); 82 } 83 84 sk_sp<SkData> data(SkData::MakeFromFD(descriptor)); 85 std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data))); 86 87 // the decoder owns the stream. 88 jobject brd = createBitmapRegionDecoder(env, std::move(stream)); 89 return brd; 90} 91 92static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, 93 jobject is, // InputStream 94 jbyteArray storage, // byte[] 95 jboolean isShareable) { 96 jobject brd = NULL; 97 // for now we don't allow shareable with java inputstreams 98 std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage)); 99 100 if (stream) { 101 // the decoder owns the stream. 102 brd = createBitmapRegionDecoder(env, std::move(stream)); 103 } 104 return brd; 105} 106 107static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, 108 jlong native_asset, // Asset 109 jboolean isShareable) { 110 Asset* asset = reinterpret_cast<Asset*>(native_asset); 111 std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset)); 112 if (NULL == stream) { 113 return NULL; 114 } 115 116 // the decoder owns the stream. 117 jobject brd = createBitmapRegionDecoder(env, std::move(stream)); 118 return brd; 119} 120 121/* 122 * nine patch not supported 123 * purgeable not supported 124 * reportSizeToVM not supported 125 */ 126static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, 127 jint inputY, jint inputWidth, jint inputHeight, jobject options) { 128 129 // Set default options. 130 int sampleSize = 1; 131 SkColorType colorType = kN32_SkColorType; 132 bool requireUnpremul = false; 133 jobject javaBitmap = NULL; 134 bool isHardware = false; 135 sk_sp<SkColorSpace> colorSpace = nullptr; 136 // Update the default options with any options supplied by the client. 137 if (NULL != options) { 138 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 139 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 140 colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 141 jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID); 142 colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace); 143 isHardware = GraphicsJNI::isHardwareConfig(env, jconfig); 144 requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 145 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 146 // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will 147 // ignore the values of these fields. 148 149 // Initialize these fields to indicate a failure. If the decode succeeds, we 150 // will update them later on. 151 env->SetIntField(options, gOptions_widthFieldID, -1); 152 env->SetIntField(options, gOptions_heightFieldID, -1); 153 env->SetObjectField(options, gOptions_mimeFieldID, 0); 154 env->SetObjectField(options, gOptions_outConfigFieldID, 0); 155 env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0); 156 } 157 158 SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 159 160 SkColorType decodeColorType = brd->computeOutputColorType(colorType); 161 sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace( 162 decodeColorType, colorSpace); 163 164 // Recycle a bitmap if possible. 165 android::Bitmap* recycledBitmap = nullptr; 166 size_t recycledBytes = 0; 167 if (javaBitmap) { 168 recycledBitmap = &bitmap::toBitmap(env, javaBitmap); 169 if (recycledBitmap->isImmutable()) { 170 ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); 171 } 172 recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap); 173 } 174 175 // Set up the pixel allocator 176 SkBRDAllocator* allocator = nullptr; 177 RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); 178 HeapAllocator heapAlloc; 179 if (javaBitmap) { 180 allocator = &recycleAlloc; 181 // We are required to match the color type of the recycled bitmap. 182 decodeColorType = recycledBitmap->info().colorType(); 183 } else { 184 allocator = &heapAlloc; 185 } 186 187 // Decode the region. 188 SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); 189 SkBitmap bitmap; 190 if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, 191 decodeColorType, requireUnpremul, decodeColorSpace)) { 192 return nullObjectReturn("Failed to decode region."); 193 } 194 195 // If the client provided options, indicate that the decode was successful. 196 if (NULL != options) { 197 env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); 198 env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); 199 200 env->SetObjectField(options, gOptions_mimeFieldID, 201 encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat())); 202 if (env->ExceptionCheck()) { 203 return nullObjectReturn("OOM in encodedFormatToString()"); 204 } 205 206 jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType); 207 if (isHardware) { 208 configID = GraphicsJNI::kHardware_LegacyBitmapConfig; 209 } 210 jobject config = env->CallStaticObjectMethod(gBitmapConfig_class, 211 gBitmapConfig_nativeToConfigMethodID, configID); 212 env->SetObjectField(options, gOptions_outConfigFieldID, config); 213 214 env->SetObjectField(options, gOptions_outColorSpaceFieldID, 215 GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType)); 216 } 217 218 // If we may have reused a bitmap, we need to indicate that the pixels have changed. 219 if (javaBitmap) { 220 recycleAlloc.copyIfNecessary(); 221 bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul); 222 return javaBitmap; 223 } 224 225 int bitmapCreateFlags = 0; 226 if (!requireUnpremul) { 227 bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; 228 } 229 if (isHardware) { 230 sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap); 231 return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags); 232 } 233 return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags); 234} 235 236static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { 237 SkBitmapRegionDecoder* brd = 238 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 239 return static_cast<jint>(brd->height()); 240} 241 242static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { 243 SkBitmapRegionDecoder* brd = 244 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 245 return static_cast<jint>(brd->width()); 246} 247 248static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { 249 SkBitmapRegionDecoder* brd = 250 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 251 delete brd; 252} 253 254/////////////////////////////////////////////////////////////////////////////// 255 256static const JNINativeMethod gBitmapRegionDecoderMethods[] = { 257 { "nativeDecodeRegion", 258 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 259 (void*)nativeDecodeRegion}, 260 261 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, 262 263 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, 264 265 { "nativeClean", "(J)V", (void*)nativeClean}, 266 267 { "nativeNewInstance", 268 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", 269 (void*)nativeNewInstanceFromByteArray 270 }, 271 272 { "nativeNewInstance", 273 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", 274 (void*)nativeNewInstanceFromStream 275 }, 276 277 { "nativeNewInstance", 278 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", 279 (void*)nativeNewInstanceFromFileDescriptor 280 }, 281 282 { "nativeNewInstance", 283 "(JZ)Landroid/graphics/BitmapRegionDecoder;", 284 (void*)nativeNewInstanceFromAsset 285 }, 286}; 287 288int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 289{ 290 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", 291 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); 292} 293