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