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