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