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