1 #define LOG_TAG "BitmapFactory" 2 3 #include "BitmapFactory.h" 4 #include "NinePatchPeeker.h" 5 #include "SkImageDecoder.h" 6 #include "SkImageRef_ashmem.h" 7 #include "SkImageRef_GlobalPool.h" 8 #include "SkPixelRef.h" 9 #include "SkStream.h" 10 #include "SkTemplates.h" 11 #include "SkUtils.h" 12 #include "CreateJavaOutputStreamAdaptor.h" 13 #include "AutoDecodeCancel.h" 14 #include "Utils.h" 15 #include "JNIHelp.h" 16 17 #include <android_runtime/AndroidRuntime.h> 18 #include <utils/Asset.h> 19 #include <utils/ResourceTypes.h> 20 #include <netinet/in.h> 21 #include <sys/mman.h> 22 #include <sys/stat.h> 23 24 jfieldID gOptions_justBoundsFieldID; 25 jfieldID gOptions_sampleSizeFieldID; 26 jfieldID gOptions_configFieldID; 27 jfieldID gOptions_mutableFieldID; 28 jfieldID gOptions_ditherFieldID; 29 jfieldID gOptions_purgeableFieldID; 30 jfieldID gOptions_shareableFieldID; 31 jfieldID gOptions_preferQualityOverSpeedFieldID; 32 jfieldID gOptions_widthFieldID; 33 jfieldID gOptions_heightFieldID; 34 jfieldID gOptions_mimeFieldID; 35 jfieldID gOptions_mCancelID; 36 jfieldID gOptions_bitmapFieldID; 37 jfieldID gBitmap_nativeBitmapFieldID; 38 39 #if 0 40 #define TRACE_BITMAP(code) code 41 #else 42 #define TRACE_BITMAP(code) 43 #endif 44 45 using namespace android; 46 47 static inline int32_t validOrNeg1(bool isValid, int32_t value) { 48 // return isValid ? value : -1; 49 SkASSERT((int)isValid == 0 || (int)isValid == 1); 50 return ((int32_t)isValid - 1) | value; 51 } 52 53 jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { 54 static const struct { 55 SkImageDecoder::Format fFormat; 56 const char* fMimeType; 57 } gMimeTypes[] = { 58 { SkImageDecoder::kBMP_Format, "image/bmp" }, 59 { SkImageDecoder::kGIF_Format, "image/gif" }, 60 { SkImageDecoder::kICO_Format, "image/x-ico" }, 61 { SkImageDecoder::kJPEG_Format, "image/jpeg" }, 62 { SkImageDecoder::kPNG_Format, "image/png" }, 63 { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" } 64 }; 65 66 const char* cstr = NULL; 67 for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) { 68 if (gMimeTypes[i].fFormat == format) { 69 cstr = gMimeTypes[i].fMimeType; 70 break; 71 } 72 } 73 74 jstring jstr = 0; 75 if (NULL != cstr) { 76 jstr = env->NewStringUTF(cstr); 77 } 78 return jstr; 79 } 80 81 static bool optionsPurgeable(JNIEnv* env, jobject options) { 82 return options != NULL && 83 env->GetBooleanField(options, gOptions_purgeableFieldID); 84 } 85 86 static bool optionsShareable(JNIEnv* env, jobject options) { 87 return options != NULL && 88 env->GetBooleanField(options, gOptions_shareableFieldID); 89 } 90 91 static bool optionsJustBounds(JNIEnv* env, jobject options) { 92 return options != NULL && 93 env->GetBooleanField(options, gOptions_justBoundsFieldID); 94 } 95 96 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, 97 int sampleSize, bool ditherImage) { 98 SkImageRef* pr; 99 // only use ashmem for large images, since mmaps come at a price 100 if (bitmap->getSize() >= 32 * 1024) { 101 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); 102 } else { 103 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); 104 } 105 pr->setDitherImage(ditherImage); 106 bitmap->setPixelRef(pr)->unref(); 107 pr->isOpaque(bitmap); 108 return pr; 109 } 110 111 // since we "may" create a purgeable imageref, we require the stream be ref'able 112 // i.e. dynamically allocated, since its lifetime may exceed the current stack 113 // frame. 114 static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 115 jobject options, bool allowPurgeable, 116 bool forcePurgeable = false) { 117 int sampleSize = 1; 118 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 119 SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; 120 bool doDither = true; 121 bool isMutable = false; 122 bool isPurgeable = forcePurgeable || 123 (allowPurgeable && optionsPurgeable(env, options)); 124 bool preferQualityOverSpeed = false; 125 jobject javaBitmap = NULL; 126 127 if (NULL != options) { 128 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 129 if (optionsJustBounds(env, options)) { 130 mode = SkImageDecoder::kDecodeBounds_Mode; 131 } 132 // initialize these, in case we fail later on 133 env->SetIntField(options, gOptions_widthFieldID, -1); 134 env->SetIntField(options, gOptions_heightFieldID, -1); 135 env->SetObjectField(options, gOptions_mimeFieldID, 0); 136 137 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 138 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 139 isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); 140 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 141 preferQualityOverSpeed = env->GetBooleanField(options, 142 gOptions_preferQualityOverSpeedFieldID); 143 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 144 } 145 146 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 147 if (NULL == decoder) { 148 return nullObjectReturn("SkImageDecoder::Factory returned null"); 149 } 150 151 decoder->setSampleSize(sampleSize); 152 decoder->setDitherImage(doDither); 153 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 154 155 NinePatchPeeker peeker(decoder); 156 JavaPixelAllocator javaAllocator(env); 157 SkBitmap* bitmap; 158 if (javaBitmap == NULL) { 159 bitmap = new SkBitmap; 160 } else { 161 if (sampleSize != 1) { 162 return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1"); 163 } 164 bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); 165 // config of supplied bitmap overrules config set in options 166 prefConfig = bitmap->getConfig(); 167 } 168 Res_png_9patch dummy9Patch; 169 170 SkAutoTDelete<SkImageDecoder> add(decoder); 171 SkAutoTDelete<SkBitmap> adb(bitmap, (javaBitmap == NULL)); 172 173 decoder->setPeeker(&peeker); 174 if (!isPurgeable) { 175 decoder->setAllocator(&javaAllocator); 176 } 177 178 AutoDecoderCancel adc(options, decoder); 179 180 // To fix the race condition in case "requestCancelDecode" 181 // happens earlier than AutoDecoderCancel object is added 182 // to the gAutoDecoderCancelMutex linked list. 183 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 184 return nullObjectReturn("gOptions_mCancelID"); 185 } 186 187 SkImageDecoder::Mode decodeMode = mode; 188 if (isPurgeable) { 189 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 190 } 191 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) { 192 return nullObjectReturn("decoder->decode returned false"); 193 } 194 195 // update options (if any) 196 if (NULL != options) { 197 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 198 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 199 // TODO: set the mimeType field with the data from the codec. 200 // but how to reuse a set of strings, rather than allocating new one 201 // each time? 202 env->SetObjectField(options, gOptions_mimeFieldID, 203 getMimeTypeString(env, decoder->getFormat())); 204 } 205 206 // if we're in justBounds mode, return now (skip the java bitmap) 207 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 208 return NULL; 209 } 210 211 jbyteArray ninePatchChunk = NULL; 212 if (peeker.fPatchIsValid) { 213 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 214 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 215 if (NULL == ninePatchChunk) { 216 return nullObjectReturn("ninePatchChunk == null"); 217 } 218 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 219 NULL); 220 if (NULL == array) { 221 return nullObjectReturn("primitive array == null"); 222 } 223 peeker.fPatch->serialize(array); 224 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 225 } 226 227 // detach bitmap from its autodeleter, since we want to own it now 228 adb.detach(); 229 230 if (padding) { 231 if (peeker.fPatchIsValid) { 232 GraphicsJNI::set_jrect(env, padding, 233 peeker.fPatch->paddingLeft, 234 peeker.fPatch->paddingTop, 235 peeker.fPatch->paddingRight, 236 peeker.fPatch->paddingBottom); 237 } else { 238 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 239 } 240 } 241 242 SkPixelRef* pr; 243 if (isPurgeable) { 244 pr = installPixelRef(bitmap, stream, sampleSize, doDither); 245 } else { 246 // if we get here, we're in kDecodePixels_Mode and will therefore 247 // already have a pixelref installed. 248 pr = bitmap->pixelRef(); 249 } 250 251 if (!isMutable) { 252 // promise we will never change our pixels (great for sharing and pictures) 253 pr->setImmutable(); 254 } 255 256 if (javaBitmap != NULL) { 257 // If a java bitmap was passed in for reuse, pass it back 258 return javaBitmap; 259 } 260 // now create the java bitmap 261 return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), 262 isMutable, ninePatchChunk); 263 } 264 265 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 266 jobject is, // InputStream 267 jbyteArray storage, // byte[] 268 jobject padding, 269 jobject options) { // BitmapFactory$Options 270 jobject bitmap = NULL; 271 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0); 272 273 if (stream) { 274 // for now we don't allow purgeable with java inputstreams 275 bitmap = doDecode(env, stream, padding, options, false); 276 stream->unref(); 277 } 278 return bitmap; 279 } 280 281 static ssize_t getFDSize(int fd) { 282 off64_t curr = ::lseek64(fd, 0, SEEK_CUR); 283 if (curr < 0) { 284 return 0; 285 } 286 size_t size = ::lseek(fd, 0, SEEK_END); 287 ::lseek64(fd, curr, SEEK_SET); 288 return size; 289 } 290 291 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 292 jobject fileDescriptor, 293 jobject padding, 294 jobject bitmapFactoryOptions) { 295 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 296 297 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 298 299 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 300 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 301 bool weOwnTheFD = false; 302 if (isPurgeable && isShareable) { 303 int newFD = ::dup(descriptor); 304 if (-1 != newFD) { 305 weOwnTheFD = true; 306 descriptor = newFD; 307 } 308 } 309 310 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 311 SkAutoUnref aur(stream); 312 if (!stream->isValid()) { 313 return NULL; 314 } 315 316 /* Restore our offset when we leave, so we can be called more than once 317 with the same descriptor. This is only required if we didn't dup the 318 file descriptor, but it is OK to do it all the time. 319 */ 320 AutoFDSeek as(descriptor); 321 322 /* Allow purgeable iff we own the FD, i.e., in the puregeable and 323 shareable case. 324 */ 325 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); 326 } 327 328 /* make a deep copy of the asset, and return it as a stream, or NULL if there 329 was an error. 330 */ 331 static SkStream* copyAssetToStream(Asset* asset) { 332 // if we could "ref/reopen" the asset, we may not need to copy it here 333 off64_t size = asset->seek(0, SEEK_SET); 334 if ((off64_t)-1 == size) { 335 SkDebugf("---- copyAsset: asset rewind failed\n"); 336 return NULL; 337 } 338 339 size = asset->getLength(); 340 if (size <= 0) { 341 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 342 return NULL; 343 } 344 345 SkStream* stream = new SkMemoryStream(size); 346 void* data = const_cast<void*>(stream->getMemoryBase()); 347 off64_t len = asset->read(data, size); 348 if (len != size) { 349 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 350 delete stream; 351 stream = NULL; 352 } 353 return stream; 354 } 355 356 static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 357 jint native_asset, // Asset 358 jobject padding, // Rect 359 jobject options) { // BitmapFactory$Options 360 SkStream* stream; 361 Asset* asset = reinterpret_cast<Asset*>(native_asset); 362 bool forcePurgeable = optionsPurgeable(env, options); 363 if (forcePurgeable) { 364 // if we could "ref/reopen" the asset, we may not need to copy it here 365 // and we could assume optionsShareable, since assets are always RO 366 stream = copyAssetToStream(asset); 367 if (NULL == stream) { 368 return NULL; 369 } 370 } else { 371 // since we know we'll be done with the asset when we return, we can 372 // just use a simple wrapper 373 stream = new AssetStreamAdaptor(asset); 374 } 375 SkAutoUnref aur(stream); 376 return doDecode(env, stream, padding, options, true, forcePurgeable); 377 } 378 379 static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 380 int offset, int length, jobject options) { 381 /* If optionsShareable() we could decide to just wrap the java array and 382 share it, but that means adding a globalref to the java array object 383 and managing its lifetime. For now we just always copy the array's data 384 if optionsPurgeable(), unless we're just decoding bounds. 385 */ 386 bool purgeable = optionsPurgeable(env, options) 387 && !optionsJustBounds(env, options); 388 AutoJavaByteArray ar(env, byteArray); 389 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); 390 SkAutoUnref aur(stream); 391 return doDecode(env, stream, NULL, options, purgeable); 392 } 393 394 static void nativeRequestCancel(JNIEnv*, jobject joptions) { 395 (void)AutoDecoderCancel::RequestCancel(joptions); 396 } 397 398 static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 399 jobject padding) { 400 401 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 402 if (array != NULL) { 403 size_t chunkSize = env->GetArrayLength(chunkObject); 404 void* storage = alloca(chunkSize); 405 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 406 memcpy(chunk, array, chunkSize); 407 android::Res_png_9patch::deserialize(chunk); 408 409 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 410 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 411 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 412 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 413 414 for (int i = 0; i < chunk->numXDivs; i++) { 415 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 416 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 417 chunk->xDivs[i]++; 418 } 419 } 420 421 for (int i = 0; i < chunk->numYDivs; i++) { 422 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 423 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 424 chunk->yDivs[i]++; 425 } 426 } 427 428 memcpy(array, chunk, chunkSize); 429 430 if (padding) { 431 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 432 chunk->paddingRight, chunk->paddingBottom); 433 } 434 435 env->ReleaseByteArrayElements(chunkObject, array, 0); 436 } 437 return chunkObject; 438 } 439 440 static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { 441 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig); 442 443 // these are the only default configs that make sense for codecs right now 444 static const SkBitmap::Config gValidDefConfig[] = { 445 SkBitmap::kRGB_565_Config, 446 SkBitmap::kARGB_8888_Config, 447 }; 448 449 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) { 450 if (config == gValidDefConfig[i]) { 451 SkImageDecoder::SetDeviceConfig(config); 452 break; 453 } 454 } 455 } 456 457 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 458 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 459 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 460 } 461 462 /////////////////////////////////////////////////////////////////////////////// 463 464 static JNINativeMethod gMethods[] = { 465 { "nativeDecodeStream", 466 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 467 (void*)nativeDecodeStream 468 }, 469 470 { "nativeDecodeFileDescriptor", 471 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 472 (void*)nativeDecodeFileDescriptor 473 }, 474 475 { "nativeDecodeAsset", 476 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 477 (void*)nativeDecodeAsset 478 }, 479 480 { "nativeDecodeByteArray", 481 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 482 (void*)nativeDecodeByteArray 483 }, 484 485 { "nativeScaleNinePatch", 486 "([BFLandroid/graphics/Rect;)[B", 487 (void*)nativeScaleNinePatch 488 }, 489 490 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, 491 492 { "nativeIsSeekable", 493 "(Ljava/io/FileDescriptor;)Z", 494 (void*)nativeIsSeekable 495 }, 496 }; 497 498 static JNINativeMethod gOptionsMethods[] = { 499 { "requestCancel", "()V", (void*)nativeRequestCancel } 500 }; 501 502 static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 503 const char fieldname[], const char type[]) { 504 jfieldID id = env->GetFieldID(clazz, fieldname, type); 505 SkASSERT(id); 506 return id; 507 } 508 509 int register_android_graphics_BitmapFactory(JNIEnv* env) { 510 jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options"); 511 SkASSERT(options_class); 512 gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap", 513 "Landroid/graphics/Bitmap;"); 514 gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z"); 515 gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I"); 516 gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig", 517 "Landroid/graphics/Bitmap$Config;"); 518 gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z"); 519 gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z"); 520 gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z"); 521 gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z"); 522 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class, 523 "inPreferQualityOverSpeed", "Z"); 524 gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I"); 525 gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I"); 526 gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;"); 527 gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z"); 528 529 jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); 530 SkASSERT(bitmap_class); 531 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I"); 532 533 int ret = AndroidRuntime::registerNativeMethods(env, 534 "android/graphics/BitmapFactory$Options", 535 gOptionsMethods, 536 SK_ARRAY_COUNT(gOptionsMethods)); 537 if (ret) { 538 return ret; 539 } 540 return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory", 541 gMethods, SK_ARRAY_COUNT(gMethods)); 542 } 543