BitmapFactory.cpp revision c70e06bbfac0d92ec218a32e35d9d7fa80f23cc9
1#define LOG_TAG "BitmapFactory"
2
3#include "SkImageDecoder.h"
4#include "SkImageRef_ashmem.h"
5#include "SkImageRef_GlobalPool.h"
6#include "SkPixelRef.h"
7#include "SkStream.h"
8#include "GraphicsJNI.h"
9#include "SkTemplates.h"
10#include "SkUtils.h"
11#include "CreateJavaOutputStreamAdaptor.h"
12
13#include <android_runtime/AndroidRuntime.h>
14#include <utils/Asset.h>
15#include <utils/ResourceTypes.h>
16#include <netinet/in.h>
17#include <sys/mman.h>
18
19static jclass gOptions_class;
20static jfieldID gOptions_justBoundsFieldID;
21static jfieldID gOptions_sampleSizeFieldID;
22static jfieldID gOptions_configFieldID;
23static jfieldID gOptions_ditherFieldID;
24static jfieldID gOptions_purgeableFieldID;
25static jfieldID gOptions_shareableFieldID;
26static jfieldID gOptions_widthFieldID;
27static jfieldID gOptions_heightFieldID;
28static jfieldID gOptions_mimeFieldID;
29static jfieldID gOptions_mCancelID;
30
31static jclass gFileDescriptor_class;
32static jfieldID gFileDescriptor_descriptor;
33
34#if 0
35    #define TRACE_BITMAP(code)  code
36#else
37    #define TRACE_BITMAP(code)
38#endif
39
40///////////////////////////////////////////////////////////////////////////////
41
42class AutoDecoderCancel {
43public:
44    AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
45    ~AutoDecoderCancel();
46
47    static bool RequestCancel(jobject options);
48
49private:
50    AutoDecoderCancel*  fNext;
51    AutoDecoderCancel*  fPrev;
52    jobject             fJOptions;  // java options object
53    SkImageDecoder*     fDecoder;
54
55#ifdef SK_DEBUG
56    static void Validate();
57#else
58    static void Validate() {}
59#endif
60};
61
62static SkMutex  gAutoDecoderCancelMutex;
63static AutoDecoderCancel* gAutoDecoderCancel;
64#ifdef SK_DEBUG
65    static int gAutoDecoderCancelCount;
66#endif
67
68AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
69                                       SkImageDecoder* decoder) {
70    fJOptions = joptions;
71    fDecoder = decoder;
72
73    if (NULL != joptions) {
74        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
75
76        // Add us as the head of the list
77        fPrev = NULL;
78        fNext = gAutoDecoderCancel;
79        if (gAutoDecoderCancel) {
80            gAutoDecoderCancel->fPrev = this;
81        }
82        gAutoDecoderCancel = this;
83
84        SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
85        Validate();
86    }
87}
88
89AutoDecoderCancel::~AutoDecoderCancel() {
90    if (NULL != fJOptions) {
91        SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
92
93        // take us out of the dllist
94        AutoDecoderCancel* prev = fPrev;
95        AutoDecoderCancel* next = fNext;
96
97        if (prev) {
98            SkASSERT(prev->fNext == this);
99            prev->fNext = next;
100        } else {
101            SkASSERT(gAutoDecoderCancel == this);
102            gAutoDecoderCancel = next;
103        }
104        if (next) {
105            SkASSERT(next->fPrev == this);
106            next->fPrev = prev;
107        }
108
109        SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
110        Validate();
111    }
112}
113
114bool AutoDecoderCancel::RequestCancel(jobject joptions) {
115    SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
116
117    Validate();
118
119    AutoDecoderCancel* pair = gAutoDecoderCancel;
120    while (pair != NULL) {
121        if (pair->fJOptions == joptions) {
122            pair->fDecoder->cancelDecode();
123            return true;
124        }
125        pair = pair->fNext;
126    }
127    return false;
128}
129
130#ifdef SK_DEBUG
131// can only call this inside a lock on gAutoDecoderCancelMutex
132void AutoDecoderCancel::Validate() {
133    const int gCount = gAutoDecoderCancelCount;
134
135    if (gCount == 0) {
136        SkASSERT(gAutoDecoderCancel == NULL);
137    } else {
138        SkASSERT(gCount > 0);
139
140        AutoDecoderCancel* curr = gAutoDecoderCancel;
141        SkASSERT(curr);
142        SkASSERT(curr->fPrev == NULL);
143
144        int count = 0;
145        while (curr) {
146            count += 1;
147            SkASSERT(count <= gCount);
148            if (curr->fPrev) {
149                SkASSERT(curr->fPrev->fNext == curr);
150            }
151            if (curr->fNext) {
152                SkASSERT(curr->fNext->fPrev == curr);
153            }
154            curr = curr->fNext;
155        }
156        SkASSERT(count == gCount);
157    }
158}
159#endif
160
161///////////////////////////////////////////////////////////////////////////////
162
163using namespace android;
164
165class NinePatchPeeker : public SkImageDecoder::Peeker {
166public:
167    NinePatchPeeker() {
168        fPatchIsValid = false;
169    }
170
171    ~NinePatchPeeker() {
172        if (fPatchIsValid) {
173            free(fPatch);
174        }
175    }
176
177    bool    fPatchIsValid;
178    Res_png_9patch*  fPatch;
179
180    virtual bool peek(const char tag[], const void* data, size_t length) {
181        if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
182            Res_png_9patch* patch = (Res_png_9patch*) data;
183            size_t patchSize = patch->serializedSize();
184            assert(length == patchSize);
185            // You have to copy the data because it is owned by the png reader
186            Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
187            memcpy(patchNew, patch, patchSize);
188            // this relies on deserialization being done in place
189            Res_png_9patch::deserialize(patchNew);
190            patchNew->fileToDevice();
191            if (fPatchIsValid) {
192                free(fPatch);
193            }
194            fPatch = patchNew;
195            //printf("9patch: (%d,%d)-(%d,%d)\n",
196            //       fPatch.sizeLeft, fPatch.sizeTop,
197            //       fPatch.sizeRight, fPatch.sizeBottom);
198            fPatchIsValid = true;
199        } else {
200            fPatch = NULL;
201        }
202        return true;    // keep on decoding
203    }
204};
205
206class AssetStreamAdaptor : public SkStream {
207public:
208    AssetStreamAdaptor(Asset* a) : fAsset(a) {}
209
210    virtual bool rewind() {
211        off_t pos = fAsset->seek(0, SEEK_SET);
212        if (pos == (off_t)-1) {
213            SkDebugf("----- fAsset->seek(rewind) failed\n");
214            return false;
215        }
216        return true;
217    }
218
219    virtual size_t read(void* buffer, size_t size) {
220        ssize_t amount;
221
222        if (NULL == buffer) {
223            if (0 == size) {  // caller is asking us for our total length
224                return fAsset->getLength();
225            }
226            // asset->seek returns new total offset
227            // we want to return amount that was skipped
228
229            off_t oldOffset = fAsset->seek(0, SEEK_CUR);
230            if (-1 == oldOffset) {
231                SkDebugf("---- fAsset->seek(oldOffset) failed\n");
232                return 0;
233            }
234            off_t newOffset = fAsset->seek(size, SEEK_CUR);
235            if (-1 == newOffset) {
236                SkDebugf("---- fAsset->seek(%d) failed\n", size);
237                return 0;
238            }
239            amount = newOffset - oldOffset;
240        } else {
241            amount = fAsset->read(buffer, size);
242            if (amount <= 0) {
243                SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount);
244            }
245        }
246
247        if (amount < 0) {
248            amount = 0;
249        }
250        return amount;
251    }
252
253private:
254    Asset*  fAsset;
255};
256
257///////////////////////////////////////////////////////////////////////////////
258
259static inline int32_t validOrNeg1(bool isValid, int32_t value) {
260//    return isValid ? value : -1;
261    SkASSERT((int)isValid == 0 || (int)isValid == 1);
262    return ((int32_t)isValid - 1) | value;
263}
264
265static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
266    static const struct {
267        SkImageDecoder::Format fFormat;
268        const char*            fMimeType;
269    } gMimeTypes[] = {
270        { SkImageDecoder::kBMP_Format,  "image/bmp" },
271        { SkImageDecoder::kGIF_Format,  "image/gif" },
272        { SkImageDecoder::kICO_Format,  "image/x-ico" },
273        { SkImageDecoder::kJPEG_Format, "image/jpeg" },
274        { SkImageDecoder::kPNG_Format,  "image/png" },
275        { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
276    };
277
278    const char* cstr = NULL;
279    for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
280        if (gMimeTypes[i].fFormat == format) {
281            cstr = gMimeTypes[i].fMimeType;
282            break;
283        }
284    }
285
286    jstring jstr = 0;
287    if (NULL != cstr) {
288        jstr = env->NewStringUTF(cstr);
289    }
290    return jstr;
291}
292
293static bool optionsPurgeable(JNIEnv* env, jobject options) {
294    return options != NULL &&
295            env->GetBooleanField(options, gOptions_purgeableFieldID);
296}
297
298static bool optionsShareable(JNIEnv* env, jobject options) {
299    return options != NULL &&
300            env->GetBooleanField(options, gOptions_shareableFieldID);
301}
302
303static jobject nullObjectReturn(const char msg[]) {
304    if (msg) {
305        SkDebugf("--- %s\n", msg);
306    }
307    return NULL;
308}
309
310static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
311                                   int sampleSize) {
312    SkPixelRef* pr;
313    // only use ashmem for large images, since mmaps come at a price
314    if (bitmap->getSize() >= 32 * 65536) {
315        pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
316    } else {
317        pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
318    }
319    bitmap->setPixelRef(pr)->unref();
320    return pr;
321}
322
323// since we "may" create a purgeable imageref, we require the stream be ref'able
324// i.e. dynamically allocated, since its lifetime may exceed the current stack
325// frame.
326static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
327                        jobject options, bool allowPurgeable) {
328    int sampleSize = 1;
329    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
330    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
331    bool doDither = true;
332    bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
333
334    if (NULL != options) {
335        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
336        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
337            mode = SkImageDecoder::kDecodeBounds_Mode;
338        }
339        // initialize these, in case we fail later on
340        env->SetIntField(options, gOptions_widthFieldID, -1);
341        env->SetIntField(options, gOptions_heightFieldID, -1);
342        env->SetObjectField(options, gOptions_mimeFieldID, 0);
343
344        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
345        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
346        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
347    }
348
349    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
350    if (NULL == decoder) {
351        return nullObjectReturn("SkImageDecoder::Factory returned null");
352    }
353
354    decoder->setSampleSize(sampleSize);
355    decoder->setDitherImage(doDither);
356
357    NinePatchPeeker     peeker;
358    JavaPixelAllocator  javaAllocator(env);
359    SkBitmap*           bitmap = new SkBitmap;
360    Res_png_9patch      dummy9Patch;
361
362    SkAutoTDelete<SkImageDecoder>   add(decoder);
363    SkAutoTDelete<SkBitmap>         adb(bitmap);
364
365    decoder->setPeeker(&peeker);
366    if (!isPurgeable) {
367        decoder->setAllocator(&javaAllocator);
368    }
369
370    AutoDecoderCancel   adc(options, decoder);
371
372    // To fix the race condition in case "requestCancelDecode"
373    // happens earlier than AutoDecoderCancel object is added
374    // to the gAutoDecoderCancelMutex linked list.
375    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
376        return nullObjectReturn("gOptions_mCancelID");;
377    }
378
379    SkImageDecoder::Mode decodeMode = mode;
380    if (isPurgeable) {
381        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
382    }
383    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
384        return nullObjectReturn("decoder->decode returned false");
385    }
386
387    // update options (if any)
388    if (NULL != options) {
389        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
390        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
391        // TODO: set the mimeType field with the data from the codec.
392        // but how to reuse a set of strings, rather than allocating new one
393        // each time?
394        env->SetObjectField(options, gOptions_mimeFieldID,
395                            getMimeTypeString(env, decoder->getFormat()));
396    }
397
398    // if we're in justBounds mode, return now (skip the java bitmap)
399    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
400        return NULL;
401    }
402
403    jbyteArray ninePatchChunk = NULL;
404    if (peeker.fPatchIsValid) {
405        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
406        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
407        if (NULL == ninePatchChunk) {
408            return nullObjectReturn("ninePatchChunk == null");
409        }
410        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
411                                                              NULL);
412        if (NULL == array) {
413            return nullObjectReturn("primitive array == null");
414        }
415        peeker.fPatch->serialize(array);
416        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
417    }
418
419    // detach bitmap from its autotdeleter, since we want to own it now
420    adb.detach();
421
422    if (padding) {
423        if (peeker.fPatchIsValid) {
424            GraphicsJNI::set_jrect(env, padding,
425                                   peeker.fPatch->paddingLeft,
426                                   peeker.fPatch->paddingTop,
427                                   peeker.fPatch->paddingRight,
428                                   peeker.fPatch->paddingBottom);
429        } else {
430            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
431        }
432    }
433
434    SkPixelRef* pr;
435    if (isPurgeable) {
436        pr = installPixelRef(bitmap, stream, sampleSize);
437    } else {
438        // if we get here, we're in kDecodePixels_Mode and will therefore
439        // already have a pixelref installed.
440        pr = bitmap->pixelRef();
441    }
442    // promise we will never change our pixels (great for sharing and pictures)
443    pr->setImmutable();
444    // now create the java bitmap
445    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
446}
447
448static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
449                                  jobject is,       // InputStream
450                                  jbyteArray storage,   // byte[]
451                                  jobject padding,
452                                  jobject options) {  // BitmapFactory$Options
453    jobject bitmap = NULL;
454    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
455
456    if (stream) {
457        // for now we don't allow purgeable with java inputstreams
458        bitmap = doDecode(env, stream, padding, options, false);
459        stream->unref();
460    }
461    return bitmap;
462}
463
464static ssize_t getFDSize(int fd) {
465    off_t curr = ::lseek(fd, 0, SEEK_CUR);
466    if (curr < 0) {
467        return 0;
468    }
469    size_t size = ::lseek(fd, 0, SEEK_END);
470    ::lseek(fd, curr, SEEK_SET);
471    return size;
472}
473
474/** Restore the file descriptor's offset in our destructor
475 */
476class AutoFDSeek {
477public:
478    AutoFDSeek(int fd) : fFD(fd) {
479        fCurr = ::lseek(fd, 0, SEEK_CUR);
480    }
481    ~AutoFDSeek() {
482        if (fCurr >= 0) {
483            ::lseek(fFD, fCurr, SEEK_SET);
484        }
485    }
486private:
487    int     fFD;
488    off_t   fCurr;
489};
490
491static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
492                                          jobject fileDescriptor,
493                                          jobject padding,
494                                          jobject bitmapFactoryOptions) {
495    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
496
497    jint descriptor = env->GetIntField(fileDescriptor,
498                                       gFileDescriptor_descriptor);
499
500    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
501    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
502    bool weOwnTheFD = false;
503    if (isPurgeable && isShareable) {
504        int newFD = ::dup(descriptor);
505        if (-1 != newFD) {
506            weOwnTheFD = true;
507            descriptor = newFD;
508        }
509    }
510
511    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
512    SkAutoUnref aur(stream);
513    if (!stream->isValid()) {
514        return NULL;
515    }
516
517    /* Restore our offset when we leave, so we can be called more than once
518       with the same descriptor. This is only required if we didn't dup the
519       file descriptor, but it is OK to do it all the time.
520    */
521    AutoFDSeek as(descriptor);
522
523    return doDecode(env, stream, padding, bitmapFactoryOptions, true);
524}
525
526/*  make a deep copy of the asset, and return it as a stream, or NULL if there
527    was an error.
528 */
529static SkStream* copyAssetToStream(Asset* asset) {
530    // if we could "ref/reopen" the asset, we may not need to copy it here
531    off_t size = asset->seek(0, SEEK_SET);
532    if ((off_t)-1 == size) {
533        SkDebugf("---- copyAsset: asset rewind failed\n");
534        return NULL;
535    }
536
537    size = asset->getLength();
538    if (size <= 0) {
539        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
540        return NULL;
541    }
542
543    SkStream* stream = new SkMemoryStream(size);
544    void* data = const_cast<void*>(stream->getMemoryBase());
545    off_t len = asset->read(data, size);
546    if (len != size) {
547        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
548        delete stream;
549        stream = NULL;
550    }
551    return stream;
552}
553
554static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
555                                 jint native_asset,    // Asset
556                                 jobject padding,       // Rect
557                                 jobject options) { // BitmapFactory$Options
558    SkStream* stream;
559    Asset* asset = reinterpret_cast<Asset*>(native_asset);
560
561    if (optionsPurgeable(env, options)) {
562        // if we could "ref/reopen" the asset, we may not need to copy it here
563        // and we could assume optionsShareable, since assets are always RO
564        stream = copyAssetToStream(asset);
565        if (NULL == stream) {
566            return NULL;
567        }
568    } else {
569        // since we know we'll be done with the asset when we return, we can
570        // just use a simple wrapper
571        stream = new AssetStreamAdaptor(asset);
572    }
573    SkAutoUnref aur(stream);
574    return doDecode(env, stream, padding, options, true);
575}
576
577static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
578                                     int offset, int length, jobject options) {
579    /*  If optionsShareable() we could decide to just wrap the java array and
580        share it, but that means adding a globalref to the java array object
581        and managing its lifetime. For now we just always copy the array's data
582        if optionsPurgeable().
583     */
584    AutoJavaByteArray ar(env, byteArray);
585    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
586                                          optionsPurgeable(env, options));
587    SkAutoUnref aur(stream);
588    return doDecode(env, stream, NULL, options, true);
589}
590
591static void nativeRequestCancel(JNIEnv*, jobject joptions) {
592    (void)AutoDecoderCancel::RequestCancel(joptions);
593}
594
595static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
596        jobject padding) {
597
598    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
599    if (array != NULL) {
600        size_t chunkSize = env->GetArrayLength(chunkObject);
601        void* storage = alloca(chunkSize);
602        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
603        memcpy(chunk, array, chunkSize);
604        android::Res_png_9patch::deserialize(chunk);
605
606        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
607        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
608        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
609        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
610
611        for (int i = 0; i < chunk->numXDivs; i++) {
612            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
613            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
614                chunk->xDivs[i]++;
615            }
616        }
617
618        for (int i = 0; i < chunk->numYDivs; i++) {
619            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
620            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
621                chunk->yDivs[i]++;
622            }
623        }
624
625        memcpy(array, chunk, chunkSize);
626
627        if (padding) {
628            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
629                    chunk->paddingRight, chunk->paddingBottom);
630        }
631
632        env->ReleaseByteArrayElements(chunkObject, array, 0);
633    }
634    return chunkObject;
635}
636
637///////////////////////////////////////////////////////////////////////////////
638
639static JNINativeMethod gMethods[] = {
640    {   "nativeDecodeStream",
641        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
642        (void*)nativeDecodeStream
643    },
644
645    {   "nativeDecodeFileDescriptor",
646        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
647        (void*)nativeDecodeFileDescriptor
648    },
649
650    {   "nativeDecodeAsset",
651        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
652        (void*)nativeDecodeAsset
653    },
654
655    {   "nativeDecodeByteArray",
656        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
657        (void*)nativeDecodeByteArray
658    },
659
660    {   "nativeScaleNinePatch",
661        "([BFLandroid/graphics/Rect;)[B",
662        (void*)nativeScaleNinePatch
663    }
664
665};
666
667static JNINativeMethod gOptionsMethods[] = {
668    {   "requestCancel", "()V", (void*)nativeRequestCancel }
669};
670
671static jclass make_globalref(JNIEnv* env, const char classname[]) {
672    jclass c = env->FindClass(classname);
673    SkASSERT(c);
674    return (jclass)env->NewGlobalRef(c);
675}
676
677static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
678                                const char fieldname[], const char type[]) {
679    jfieldID id = env->GetFieldID(clazz, fieldname, type);
680    SkASSERT(id);
681    return id;
682}
683
684#define kClassPathName  "android/graphics/BitmapFactory"
685
686#define RETURN_ERR_IF_NULL(value) \
687    do { if (!(value)) { assert(0); return -1; } } while (false)
688
689int register_android_graphics_BitmapFactory(JNIEnv* env);
690int register_android_graphics_BitmapFactory(JNIEnv* env) {
691    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
692    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
693    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
694    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
695            "Landroid/graphics/Bitmap$Config;");
696    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
697    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
698    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
699    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
700    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
701    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
702    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
703
704    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
705    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
706
707    int ret = AndroidRuntime::registerNativeMethods(env,
708                                    "android/graphics/BitmapFactory$Options",
709                                    gOptionsMethods,
710                                    SK_ARRAY_COUNT(gOptionsMethods));
711    if (ret) {
712        return ret;
713    }
714    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
715                                         gMethods, SK_ARRAY_COUNT(gMethods));
716}
717