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