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