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 <nativehelper/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 44 using namespace android; 45 46 static 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 58 static 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 72 static 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 92 static 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 107 static 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 */ 126 static 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 // Recycle a bitmap if possible. 159 android::Bitmap* recycledBitmap = nullptr; 160 size_t recycledBytes = 0; 161 if (javaBitmap) { 162 recycledBitmap = &bitmap::toBitmap(env, javaBitmap); 163 if (recycledBitmap->isImmutable()) { 164 ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); 165 } 166 recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap); 167 } 168 169 SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 170 SkColorType decodeColorType = brd->computeOutputColorType(colorType); 171 172 // Set up the pixel allocator 173 SkBRDAllocator* allocator = nullptr; 174 RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); 175 HeapAllocator heapAlloc; 176 if (javaBitmap) { 177 allocator = &recycleAlloc; 178 // We are required to match the color type of the recycled bitmap. 179 decodeColorType = recycledBitmap->info().colorType(); 180 } else { 181 allocator = &heapAlloc; 182 } 183 184 sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace( 185 decodeColorType, colorSpace); 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 236 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { 237 SkBitmapRegionDecoder* brd = 238 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 239 return static_cast<jint>(brd->height()); 240 } 241 242 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { 243 SkBitmapRegionDecoder* brd = 244 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 245 return static_cast<jint>(brd->width()); 246 } 247 248 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { 249 SkBitmapRegionDecoder* brd = 250 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 251 delete brd; 252 } 253 254 /////////////////////////////////////////////////////////////////////////////// 255 256 static 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 288 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 289 { 290 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", 291 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); 292 } 293