BitmapFactory.cpp revision 37f74cad46c6f1799aec3c52e8f47598237f43d4
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_ditherFieldID; 27jfieldID gOptions_purgeableFieldID; 28jfieldID gOptions_shareableFieldID; 29jfieldID gOptions_nativeAllocFieldID; 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 bool optionsReportSizeToVM(JNIEnv* env, jobject options) { 159 return NULL == options || 160 !env->GetBooleanField(options, gOptions_nativeAllocFieldID); 161} 162 163 164static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, 165 int sampleSize, bool ditherImage) { 166 SkImageRef* pr; 167 // only use ashmem for large images, since mmaps come at a price 168 if (bitmap->getSize() >= 32 * 1024) { 169 pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); 170 } else { 171 pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); 172 } 173 pr->setDitherImage(ditherImage); 174 bitmap->setPixelRef(pr)->unref(); 175 pr->isOpaque(bitmap); 176 return pr; 177} 178 179// since we "may" create a purgeable imageref, we require the stream be ref'able 180// i.e. dynamically allocated, since its lifetime may exceed the current stack 181// frame. 182static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, 183 jobject options, bool allowPurgeable, 184 bool forcePurgeable = false) { 185 int sampleSize = 1; 186 SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; 187 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 188 bool doDither = true; 189 bool isPurgeable = forcePurgeable || 190 (allowPurgeable && optionsPurgeable(env, options)); 191 bool reportSizeToVM = optionsReportSizeToVM(env, options); 192 bool preferQualityOverSpeed = false; 193 jobject javaBitmap = NULL; 194 195 if (NULL != options) { 196 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 197 if (optionsJustBounds(env, options)) { 198 mode = SkImageDecoder::kDecodeBounds_Mode; 199 } 200 // initialize these, in case we fail later on 201 env->SetIntField(options, gOptions_widthFieldID, -1); 202 env->SetIntField(options, gOptions_heightFieldID, -1); 203 env->SetObjectField(options, gOptions_mimeFieldID, 0); 204 205 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 206 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 207 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 208 preferQualityOverSpeed = env->GetBooleanField(options, 209 gOptions_preferQualityOverSpeedFieldID); 210 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 211 } 212 213 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 214 if (NULL == decoder) { 215 return nullObjectReturn("SkImageDecoder::Factory returned null"); 216 } 217 218 decoder->setSampleSize(sampleSize); 219 decoder->setDitherImage(doDither); 220 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 221 222 NinePatchPeeker peeker(decoder); 223 JavaPixelAllocator javaAllocator(env, reportSizeToVM); 224 SkBitmap* bitmap; 225 if (javaBitmap == NULL) { 226 bitmap = new SkBitmap; 227 } else { 228 bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); 229 // config of supplied bitmap overrules config set in options 230 prefConfig = bitmap->getConfig(); 231 } 232 Res_png_9patch dummy9Patch; 233 234 SkAutoTDelete<SkImageDecoder> add(decoder); 235 SkAutoTDelete<SkBitmap> adb(bitmap); 236 237 decoder->setPeeker(&peeker); 238 if (!isPurgeable) { 239 decoder->setAllocator(&javaAllocator); 240 } 241 242 AutoDecoderCancel adc(options, decoder); 243 244 // To fix the race condition in case "requestCancelDecode" 245 // happens earlier than AutoDecoderCancel object is added 246 // to the gAutoDecoderCancelMutex linked list. 247 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 248 return nullObjectReturn("gOptions_mCancelID"); 249 } 250 251 SkImageDecoder::Mode decodeMode = mode; 252 if (isPurgeable) { 253 decodeMode = SkImageDecoder::kDecodeBounds_Mode; 254 } 255 if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) { 256 return nullObjectReturn("decoder->decode returned false"); 257 } 258 259 // update options (if any) 260 if (NULL != options) { 261 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 262 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 263 // TODO: set the mimeType field with the data from the codec. 264 // but how to reuse a set of strings, rather than allocating new one 265 // each time? 266 env->SetObjectField(options, gOptions_mimeFieldID, 267 getMimeTypeString(env, decoder->getFormat())); 268 } 269 270 // if we're in justBounds mode, return now (skip the java bitmap) 271 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 272 return NULL; 273 } 274 275 jbyteArray ninePatchChunk = NULL; 276 if (peeker.fPatchIsValid) { 277 size_t ninePatchArraySize = peeker.fPatch->serializedSize(); 278 ninePatchChunk = env->NewByteArray(ninePatchArraySize); 279 if (NULL == ninePatchChunk) { 280 return nullObjectReturn("ninePatchChunk == null"); 281 } 282 jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, 283 NULL); 284 if (NULL == array) { 285 return nullObjectReturn("primitive array == null"); 286 } 287 peeker.fPatch->serialize(array); 288 env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 289 } 290 291 // detach bitmap from its autotdeleter, since we want to own it now 292 adb.detach(); 293 294 if (padding) { 295 if (peeker.fPatchIsValid) { 296 GraphicsJNI::set_jrect(env, padding, 297 peeker.fPatch->paddingLeft, 298 peeker.fPatch->paddingTop, 299 peeker.fPatch->paddingRight, 300 peeker.fPatch->paddingBottom); 301 } else { 302 GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); 303 } 304 } 305 306 SkPixelRef* pr; 307 if (isPurgeable) { 308 pr = installPixelRef(bitmap, stream, sampleSize, doDither); 309 } else { 310 // if we get here, we're in kDecodePixels_Mode and will therefore 311 // already have a pixelref installed. 312 pr = bitmap->pixelRef(); 313 } 314 // promise we will never change our pixels (great for sharing and pictures) 315 pr->setImmutable(); 316 317 if (javaBitmap != NULL) { 318 // If a java bitmap was passed in for reuse, pass it back 319 return javaBitmap; 320 } 321 // now create the java bitmap 322 return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); 323} 324 325static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, 326 jobject is, // InputStream 327 jbyteArray storage, // byte[] 328 jobject padding, 329 jobject options) { // BitmapFactory$Options 330 jobject bitmap = NULL; 331 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0); 332 333 if (stream) { 334 // for now we don't allow purgeable with java inputstreams 335 bitmap = doDecode(env, stream, padding, options, false); 336 stream->unref(); 337 } 338 return bitmap; 339} 340 341static ssize_t getFDSize(int fd) { 342 off64_t curr = ::lseek64(fd, 0, SEEK_CUR); 343 if (curr < 0) { 344 return 0; 345 } 346 size_t size = ::lseek(fd, 0, SEEK_END); 347 ::lseek64(fd, curr, SEEK_SET); 348 return size; 349} 350 351static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, 352 jobject fileDescriptor, 353 jobject padding, 354 jobject bitmapFactoryOptions) { 355 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 356 357 jint descriptor = env->GetIntField(fileDescriptor, 358 gFileDescriptor_descriptor); 359 360 bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); 361 bool isShareable = optionsShareable(env, bitmapFactoryOptions); 362 bool weOwnTheFD = false; 363 if (isPurgeable && isShareable) { 364 int newFD = ::dup(descriptor); 365 if (-1 != newFD) { 366 weOwnTheFD = true; 367 descriptor = newFD; 368 } 369 } 370 371 SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); 372 SkAutoUnref aur(stream); 373 if (!stream->isValid()) { 374 return NULL; 375 } 376 377 /* Restore our offset when we leave, so we can be called more than once 378 with the same descriptor. This is only required if we didn't dup the 379 file descriptor, but it is OK to do it all the time. 380 */ 381 AutoFDSeek as(descriptor); 382 383 /* Allow purgeable iff we own the FD, i.e., in the puregeable and 384 shareable case. 385 */ 386 return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); 387} 388 389/* make a deep copy of the asset, and return it as a stream, or NULL if there 390 was an error. 391 */ 392static SkStream* copyAssetToStream(Asset* asset) { 393 // if we could "ref/reopen" the asset, we may not need to copy it here 394 off64_t size = asset->seek(0, SEEK_SET); 395 if ((off64_t)-1 == size) { 396 SkDebugf("---- copyAsset: asset rewind failed\n"); 397 return NULL; 398 } 399 400 size = asset->getLength(); 401 if (size <= 0) { 402 SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); 403 return NULL; 404 } 405 406 SkStream* stream = new SkMemoryStream(size); 407 void* data = const_cast<void*>(stream->getMemoryBase()); 408 off64_t len = asset->read(data, size); 409 if (len != size) { 410 SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); 411 delete stream; 412 stream = NULL; 413 } 414 return stream; 415} 416 417static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, 418 jint native_asset, // Asset 419 jobject padding, // Rect 420 jobject options) { // BitmapFactory$Options 421 SkStream* stream; 422 Asset* asset = reinterpret_cast<Asset*>(native_asset); 423 bool forcePurgeable = optionsPurgeable(env, options); 424 if (forcePurgeable) { 425 // if we could "ref/reopen" the asset, we may not need to copy it here 426 // and we could assume optionsShareable, since assets are always RO 427 stream = copyAssetToStream(asset); 428 if (NULL == stream) { 429 return NULL; 430 } 431 } else { 432 // since we know we'll be done with the asset when we return, we can 433 // just use a simple wrapper 434 stream = new AssetStreamAdaptor(asset); 435 } 436 SkAutoUnref aur(stream); 437 return doDecode(env, stream, padding, options, true, forcePurgeable); 438} 439 440static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 441 int offset, int length, jobject options) { 442 /* If optionsShareable() we could decide to just wrap the java array and 443 share it, but that means adding a globalref to the java array object 444 and managing its lifetime. For now we just always copy the array's data 445 if optionsPurgeable(), unless we're just decoding bounds. 446 */ 447 bool purgeable = optionsPurgeable(env, options) 448 && !optionsJustBounds(env, options); 449 AutoJavaByteArray ar(env, byteArray); 450 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); 451 SkAutoUnref aur(stream); 452 return doDecode(env, stream, NULL, options, purgeable); 453} 454 455static void nativeRequestCancel(JNIEnv*, jobject joptions) { 456 (void)AutoDecoderCancel::RequestCancel(joptions); 457} 458 459static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale, 460 jobject padding) { 461 462 jbyte* array = env->GetByteArrayElements(chunkObject, 0); 463 if (array != NULL) { 464 size_t chunkSize = env->GetArrayLength(chunkObject); 465 void* storage = alloca(chunkSize); 466 android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage); 467 memcpy(chunk, array, chunkSize); 468 android::Res_png_9patch::deserialize(chunk); 469 470 chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); 471 chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); 472 chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); 473 chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); 474 475 for (int i = 0; i < chunk->numXDivs; i++) { 476 chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f); 477 if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) { 478 chunk->xDivs[i]++; 479 } 480 } 481 482 for (int i = 0; i < chunk->numYDivs; i++) { 483 chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f); 484 if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) { 485 chunk->yDivs[i]++; 486 } 487 } 488 489 memcpy(array, chunk, chunkSize); 490 491 if (padding) { 492 GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop, 493 chunk->paddingRight, chunk->paddingBottom); 494 } 495 496 env->ReleaseByteArrayElements(chunkObject, array, 0); 497 } 498 return chunkObject; 499} 500 501static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) { 502 SkBitmap::Config config = static_cast<SkBitmap::Config>(nativeConfig); 503 504 // these are the only default configs that make sense for codecs right now 505 static const SkBitmap::Config gValidDefConfig[] = { 506 SkBitmap::kRGB_565_Config, 507 SkBitmap::kARGB_8888_Config, 508 }; 509 510 for (size_t i = 0; i < SK_ARRAY_COUNT(gValidDefConfig); i++) { 511 if (config == gValidDefConfig[i]) { 512 SkImageDecoder::SetDeviceConfig(config); 513 break; 514 } 515 } 516} 517 518/////////////////////////////////////////////////////////////////////////////// 519 520static JNINativeMethod gMethods[] = { 521 { "nativeDecodeStream", 522 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 523 (void*)nativeDecodeStream 524 }, 525 526 { "nativeDecodeFileDescriptor", 527 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 528 (void*)nativeDecodeFileDescriptor 529 }, 530 531 { "nativeDecodeAsset", 532 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 533 (void*)nativeDecodeAsset 534 }, 535 536 { "nativeDecodeByteArray", 537 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 538 (void*)nativeDecodeByteArray 539 }, 540 541 { "nativeScaleNinePatch", 542 "([BFLandroid/graphics/Rect;)[B", 543 (void*)nativeScaleNinePatch 544 }, 545 546 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, 547}; 548 549static JNINativeMethod gOptionsMethods[] = { 550 { "requestCancel", "()V", (void*)nativeRequestCancel } 551}; 552 553static jclass make_globalref(JNIEnv* env, const char classname[]) { 554 jclass c = env->FindClass(classname); 555 SkASSERT(c); 556 return (jclass)env->NewGlobalRef(c); 557} 558 559static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 560 const char fieldname[], const char type[]) { 561 jfieldID id = env->GetFieldID(clazz, fieldname, type); 562 SkASSERT(id); 563 return id; 564} 565 566#define kClassPathName "android/graphics/BitmapFactory" 567 568#define RETURN_ERR_IF_NULL(value) \ 569 do { if (!(value)) { assert(0); return -1; } } while (false) 570 571int register_android_graphics_BitmapFactory(JNIEnv* env); 572int register_android_graphics_BitmapFactory(JNIEnv* env) { 573 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 574 gOptions_bitmapFieldID = getFieldIDCheck(env, gOptions_class, "inBitmap", 575 "Landroid/graphics/Bitmap;"); 576 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 577 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 578 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 579 "Landroid/graphics/Bitmap$Config;"); 580 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 581 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 582 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 583 gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z"); 584 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class, 585 "inPreferQualityOverSpeed", "Z"); 586 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 587 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 588 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 589 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 590 591 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 592 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 593 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 594 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 595 596 int ret = AndroidRuntime::registerNativeMethods(env, 597 "android/graphics/BitmapFactory$Options", 598 gOptionsMethods, 599 SK_ARRAY_COUNT(gOptionsMethods)); 600 if (ret) { 601 return ret; 602 } 603 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 604 gMethods, SK_ARRAY_COUNT(gMethods)); 605} 606