BitmapFactory.cpp revision 17154417e8ad488d18d9133bf802f598e7506483
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, bool ditherImage) {
318    SkImageRef* 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    pr->setDitherImage(ditherImage);
326    bitmap->setPixelRef(pr)->unref();
327    return pr;
328}
329
330// since we "may" create a purgeable imageref, we require the stream be ref'able
331// i.e. dynamically allocated, since its lifetime may exceed the current stack
332// frame.
333static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
334                        jobject options, bool allowPurgeable) {
335    int sampleSize = 1;
336    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
337    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
338    bool doDither = true;
339    bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
340    bool reportSizeToVM = optionsReportSizeToVM(env, options);
341
342    if (NULL != options) {
343        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
344        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
345            mode = SkImageDecoder::kDecodeBounds_Mode;
346        }
347        // initialize these, in case we fail later on
348        env->SetIntField(options, gOptions_widthFieldID, -1);
349        env->SetIntField(options, gOptions_heightFieldID, -1);
350        env->SetObjectField(options, gOptions_mimeFieldID, 0);
351
352        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
353        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
354        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
355    }
356
357    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
358    if (NULL == decoder) {
359        return nullObjectReturn("SkImageDecoder::Factory returned null");
360    }
361
362    decoder->setSampleSize(sampleSize);
363    decoder->setDitherImage(doDither);
364
365    NinePatchPeeker     peeker;
366    JavaPixelAllocator  javaAllocator(env, reportSizeToVM);
367    SkBitmap*           bitmap = new SkBitmap;
368    Res_png_9patch      dummy9Patch;
369
370    SkAutoTDelete<SkImageDecoder>   add(decoder);
371    SkAutoTDelete<SkBitmap>         adb(bitmap);
372
373    decoder->setPeeker(&peeker);
374    if (!isPurgeable) {
375        decoder->setAllocator(&javaAllocator);
376    }
377
378    AutoDecoderCancel   adc(options, decoder);
379
380    // To fix the race condition in case "requestCancelDecode"
381    // happens earlier than AutoDecoderCancel object is added
382    // to the gAutoDecoderCancelMutex linked list.
383    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
384        return nullObjectReturn("gOptions_mCancelID");;
385    }
386
387    SkImageDecoder::Mode decodeMode = mode;
388    if (isPurgeable) {
389        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
390    }
391    if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) {
392        return nullObjectReturn("decoder->decode returned false");
393    }
394
395    // update options (if any)
396    if (NULL != options) {
397        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
398        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
399        // TODO: set the mimeType field with the data from the codec.
400        // but how to reuse a set of strings, rather than allocating new one
401        // each time?
402        env->SetObjectField(options, gOptions_mimeFieldID,
403                            getMimeTypeString(env, decoder->getFormat()));
404    }
405
406    // if we're in justBounds mode, return now (skip the java bitmap)
407    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
408        return NULL;
409    }
410
411    jbyteArray ninePatchChunk = NULL;
412    if (peeker.fPatchIsValid) {
413        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
414        ninePatchChunk = env->NewByteArray(ninePatchArraySize);
415        if (NULL == ninePatchChunk) {
416            return nullObjectReturn("ninePatchChunk == null");
417        }
418        jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
419                                                              NULL);
420        if (NULL == array) {
421            return nullObjectReturn("primitive array == null");
422        }
423        peeker.fPatch->serialize(array);
424        env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
425    }
426
427    // detach bitmap from its autotdeleter, since we want to own it now
428    adb.detach();
429
430    if (padding) {
431        if (peeker.fPatchIsValid) {
432            GraphicsJNI::set_jrect(env, padding,
433                                   peeker.fPatch->paddingLeft,
434                                   peeker.fPatch->paddingTop,
435                                   peeker.fPatch->paddingRight,
436                                   peeker.fPatch->paddingBottom);
437        } else {
438            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
439        }
440    }
441
442    SkPixelRef* pr;
443    if (isPurgeable) {
444        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
445    } else {
446        // if we get here, we're in kDecodePixels_Mode and will therefore
447        // already have a pixelref installed.
448        pr = bitmap->pixelRef();
449    }
450    // promise we will never change our pixels (great for sharing and pictures)
451    pr->setImmutable();
452    // now create the java bitmap
453    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
454}
455
456static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
457                                  jobject is,       // InputStream
458                                  jbyteArray storage,   // byte[]
459                                  jobject padding,
460                                  jobject options) {  // BitmapFactory$Options
461    jobject bitmap = NULL;
462    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
463
464    if (stream) {
465        // for now we don't allow purgeable with java inputstreams
466        bitmap = doDecode(env, stream, padding, options, false);
467        stream->unref();
468    }
469    return bitmap;
470}
471
472static ssize_t getFDSize(int fd) {
473    off_t curr = ::lseek(fd, 0, SEEK_CUR);
474    if (curr < 0) {
475        return 0;
476    }
477    size_t size = ::lseek(fd, 0, SEEK_END);
478    ::lseek(fd, curr, SEEK_SET);
479    return size;
480}
481
482/** Restore the file descriptor's offset in our destructor
483 */
484class AutoFDSeek {
485public:
486    AutoFDSeek(int fd) : fFD(fd) {
487        fCurr = ::lseek(fd, 0, SEEK_CUR);
488    }
489    ~AutoFDSeek() {
490        if (fCurr >= 0) {
491            ::lseek(fFD, fCurr, SEEK_SET);
492        }
493    }
494private:
495    int     fFD;
496    off_t   fCurr;
497};
498
499static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
500                                          jobject fileDescriptor,
501                                          jobject padding,
502                                          jobject bitmapFactoryOptions) {
503    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
504
505    jint descriptor = env->GetIntField(fileDescriptor,
506                                       gFileDescriptor_descriptor);
507
508    bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
509    bool isShareable = optionsShareable(env, bitmapFactoryOptions);
510    bool weOwnTheFD = false;
511    if (isPurgeable && isShareable) {
512        int newFD = ::dup(descriptor);
513        if (-1 != newFD) {
514            weOwnTheFD = true;
515            descriptor = newFD;
516        }
517    }
518
519    SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
520    SkAutoUnref aur(stream);
521    if (!stream->isValid()) {
522        return NULL;
523    }
524
525    /* Restore our offset when we leave, so we can be called more than once
526       with the same descriptor. This is only required if we didn't dup the
527       file descriptor, but it is OK to do it all the time.
528    */
529    AutoFDSeek as(descriptor);
530
531    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
532       shareable case.
533    */
534    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
535}
536
537/*  make a deep copy of the asset, and return it as a stream, or NULL if there
538    was an error.
539 */
540static SkStream* copyAssetToStream(Asset* asset) {
541    // if we could "ref/reopen" the asset, we may not need to copy it here
542    off_t size = asset->seek(0, SEEK_SET);
543    if ((off_t)-1 == size) {
544        SkDebugf("---- copyAsset: asset rewind failed\n");
545        return NULL;
546    }
547
548    size = asset->getLength();
549    if (size <= 0) {
550        SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
551        return NULL;
552    }
553
554    SkStream* stream = new SkMemoryStream(size);
555    void* data = const_cast<void*>(stream->getMemoryBase());
556    off_t len = asset->read(data, size);
557    if (len != size) {
558        SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
559        delete stream;
560        stream = NULL;
561    }
562    return stream;
563}
564
565static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
566                                 jint native_asset,    // Asset
567                                 jobject padding,       // Rect
568                                 jobject options) { // BitmapFactory$Options
569    SkStream* stream;
570    Asset* asset = reinterpret_cast<Asset*>(native_asset);
571
572    if (optionsPurgeable(env, options)) {
573        // if we could "ref/reopen" the asset, we may not need to copy it here
574        // and we could assume optionsShareable, since assets are always RO
575        stream = copyAssetToStream(asset);
576        if (NULL == stream) {
577            return NULL;
578        }
579    } else {
580        // since we know we'll be done with the asset when we return, we can
581        // just use a simple wrapper
582        stream = new AssetStreamAdaptor(asset);
583    }
584    SkAutoUnref aur(stream);
585    return doDecode(env, stream, padding, options, true);
586}
587
588static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
589                                     int offset, int length, jobject options) {
590    /*  If optionsShareable() we could decide to just wrap the java array and
591        share it, but that means adding a globalref to the java array object
592        and managing its lifetime. For now we just always copy the array's data
593        if optionsPurgeable().
594     */
595    AutoJavaByteArray ar(env, byteArray);
596    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
597                                          optionsPurgeable(env, options));
598    SkAutoUnref aur(stream);
599    return doDecode(env, stream, NULL, options, true);
600}
601
602static void nativeRequestCancel(JNIEnv*, jobject joptions) {
603    (void)AutoDecoderCancel::RequestCancel(joptions);
604}
605
606static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
607        jobject padding) {
608
609    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
610    if (array != NULL) {
611        size_t chunkSize = env->GetArrayLength(chunkObject);
612        void* storage = alloca(chunkSize);
613        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
614        memcpy(chunk, array, chunkSize);
615        android::Res_png_9patch::deserialize(chunk);
616
617        chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
618        chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
619        chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
620        chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
621
622        for (int i = 0; i < chunk->numXDivs; i++) {
623            chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
624            if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
625                chunk->xDivs[i]++;
626            }
627        }
628
629        for (int i = 0; i < chunk->numYDivs; i++) {
630            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
631            if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
632                chunk->yDivs[i]++;
633            }
634        }
635
636        memcpy(array, chunk, chunkSize);
637
638        if (padding) {
639            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
640                    chunk->paddingRight, chunk->paddingBottom);
641        }
642
643        env->ReleaseByteArrayElements(chunkObject, array, 0);
644    }
645    return chunkObject;
646}
647
648///////////////////////////////////////////////////////////////////////////////
649
650static JNINativeMethod gMethods[] = {
651    {   "nativeDecodeStream",
652        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
653        (void*)nativeDecodeStream
654    },
655
656    {   "nativeDecodeFileDescriptor",
657        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
658        (void*)nativeDecodeFileDescriptor
659    },
660
661    {   "nativeDecodeAsset",
662        "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
663        (void*)nativeDecodeAsset
664    },
665
666    {   "nativeDecodeByteArray",
667        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
668        (void*)nativeDecodeByteArray
669    },
670
671    {   "nativeScaleNinePatch",
672        "([BFLandroid/graphics/Rect;)[B",
673        (void*)nativeScaleNinePatch
674    }
675
676};
677
678static JNINativeMethod gOptionsMethods[] = {
679    {   "requestCancel", "()V", (void*)nativeRequestCancel }
680};
681
682static jclass make_globalref(JNIEnv* env, const char classname[]) {
683    jclass c = env->FindClass(classname);
684    SkASSERT(c);
685    return (jclass)env->NewGlobalRef(c);
686}
687
688static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
689                                const char fieldname[], const char type[]) {
690    jfieldID id = env->GetFieldID(clazz, fieldname, type);
691    SkASSERT(id);
692    return id;
693}
694
695#define kClassPathName  "android/graphics/BitmapFactory"
696
697#define RETURN_ERR_IF_NULL(value) \
698    do { if (!(value)) { assert(0); return -1; } } while (false)
699
700int register_android_graphics_BitmapFactory(JNIEnv* env);
701int register_android_graphics_BitmapFactory(JNIEnv* env) {
702    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
703    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
704    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
705    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
706            "Landroid/graphics/Bitmap$Config;");
707    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
708    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
709    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
710    gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
711    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
712    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
713    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
714    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
715
716    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
717    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
718
719    int ret = AndroidRuntime::registerNativeMethods(env,
720                                    "android/graphics/BitmapFactory$Options",
721                                    gOptionsMethods,
722                                    SK_ARRAY_COUNT(gOptionsMethods));
723    if (ret) {
724        return ret;
725    }
726    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
727                                         gMethods, SK_ARRAY_COUNT(gMethods));
728}
729