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