BitmapFactory.cpp revision 4b26247e8b45850afc78e414a7007266dbdc5d18
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 520static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { 521 jint descriptor = env->GetIntField(fileDescriptor, gFileDescriptor_descriptor); 522 return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; 523} 524 525/////////////////////////////////////////////////////////////////////////////// 526 527static JNINativeMethod gMethods[] = { 528 { "nativeDecodeStream", 529 "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 530 (void*)nativeDecodeStream 531 }, 532 533 { "nativeDecodeFileDescriptor", 534 "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 535 (void*)nativeDecodeFileDescriptor 536 }, 537 538 { "nativeDecodeAsset", 539 "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 540 (void*)nativeDecodeAsset 541 }, 542 543 { "nativeDecodeByteArray", 544 "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 545 (void*)nativeDecodeByteArray 546 }, 547 548 { "nativeScaleNinePatch", 549 "([BFLandroid/graphics/Rect;)[B", 550 (void*)nativeScaleNinePatch 551 }, 552 553 { "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig }, 554 555 { "nativeIsSeekable", 556 "(Ljava/io/FileDescriptor;)Z", 557 (void*)nativeIsSeekable 558 }, 559}; 560 561static JNINativeMethod gOptionsMethods[] = { 562 { "requestCancel", "()V", (void*)nativeRequestCancel } 563}; 564 565static jclass make_globalref(JNIEnv* env, const char classname[]) { 566 jclass c = env->FindClass(classname); 567 SkASSERT(c); 568 return (jclass)env->NewGlobalRef(c); 569} 570 571static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 572 const char fieldname[], const char type[]) { 573 jfieldID id = env->GetFieldID(clazz, fieldname, type); 574 SkASSERT(id); 575 return id; 576} 577 578#define kClassPathName "android/graphics/BitmapFactory" 579 580#define RETURN_ERR_IF_NULL(value) \ 581 do { if (!(value)) { assert(0); return -1; } } while (false) 582 583int register_android_graphics_BitmapFactory(JNIEnv* env); 584int register_android_graphics_BitmapFactory(JNIEnv* env) { 585 gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options"); 586 gOptions_bitmapFieldID = getFieldIDCheck(env, gOptions_class, "inBitmap", 587 "Landroid/graphics/Bitmap;"); 588 gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z"); 589 gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I"); 590 gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", 591 "Landroid/graphics/Bitmap$Config;"); 592 gOptions_mutableFieldID = getFieldIDCheck(env, gOptions_class, "inMutable", "Z"); 593 gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); 594 gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); 595 gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); 596 gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class, 597 "inPreferQualityOverSpeed", "Z"); 598 gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); 599 gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); 600 gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); 601 gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z"); 602 603 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 604 gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 605 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 606 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 607 608 int ret = AndroidRuntime::registerNativeMethods(env, 609 "android/graphics/BitmapFactory$Options", 610 gOptionsMethods, 611 SK_ARRAY_COUNT(gOptionsMethods)); 612 if (ret) { 613 return ret; 614 } 615 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 616 gMethods, SK_ARRAY_COUNT(gMethods)); 617} 618