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